2020-11-18 08:10:08

by Wendy Liang

[permalink] [raw]
Subject: [PATCH 0/9] Xilinx AI engine kernel driver

AI engine is the acceleration engine provided by Xilinx. These engines
provide high compute density for vector-based algorithms, and flexible
custom compute and data movement. It has core tiles for compute and
shim tiles to interface the FPGA fabric.

You can check the AI engine architecture document for more hardware details:
https://www.xilinx.com/support/documentation/architecture-manuals/am009-versal-ai-engine.pdf

This patch series adds a Linux kernel driver to manage the Xilinx AI
engine array device and AI engine partitions (groups of AI engine tiles
dedicated to an application).

Izhar Ameer Shaikh (1):
firmware: xilinx: Add IOCTL support for AIE ISR Clear

Nishad Saraf (2):
misc: xilinx-ai-engine: Add support to request device management
services
misc: xilinx-ai-engine: Add support for servicing error interrupts

Wendy Liang (6):
dt-binding: soc: xilinx: ai-engine: Add AI engine binding
misc: Add Xilinx AI engine device driver
misc: xilinx-ai-engine: Implement AI engine cleanup sequence
misc: xilinx-ai-engine: expose AI engine tile memories to userspace
misc: xilinx-ai-engine: add setting shim dma bd operation
misc: xilinx-ai-engine: add request and release tiles

.../bindings/soc/xilinx/xlnx,ai-engine.yaml | 119 ++++
MAINTAINERS | 8 +
drivers/firmware/xilinx/zynqmp.c | 14 +
drivers/misc/Kconfig | 12 +
drivers/misc/Makefile | 1 +
drivers/misc/xilinx-ai-engine/Makefile | 16 +
drivers/misc/xilinx-ai-engine/ai-engine-aie.c | 608 +++++++++++++++++++
drivers/misc/xilinx-ai-engine/ai-engine-clock.c | 244 ++++++++
drivers/misc/xilinx-ai-engine/ai-engine-dev.c | 492 +++++++++++++++
drivers/misc/xilinx-ai-engine/ai-engine-dma.c | 481 +++++++++++++++
drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 519 ++++++++++++++++
.../misc/xilinx-ai-engine/ai-engine-interrupt.c | 661 +++++++++++++++++++++
drivers/misc/xilinx-ai-engine/ai-engine-mem.c | 274 +++++++++
drivers/misc/xilinx-ai-engine/ai-engine-part.c | 635 ++++++++++++++++++++
drivers/misc/xilinx-ai-engine/ai-engine-res.c | 219 +++++++
drivers/misc/xilinx-ai-engine/ai-engine-reset.c | 159 +++++
include/linux/firmware/xlnx-zynqmp.h | 8 +
include/uapi/linux/xlnx-ai-engine.h | 236 ++++++++
18 files changed, 4706 insertions(+)
create mode 100644 Documentation/devicetree/bindings/soc/xilinx/xlnx,ai-engine.yaml
create mode 100644 drivers/misc/xilinx-ai-engine/Makefile
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-aie.c
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-clock.c
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dev.c
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dma.c
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-internal.h
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-interrupt.c
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-mem.c
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-part.c
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-res.c
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-reset.c
create mode 100644 include/uapi/linux/xlnx-ai-engine.h

--
2.7.4


2020-11-18 08:10:41

by Wendy Liang

[permalink] [raw]
Subject: [PATCH 5/9] misc: xilinx-ai-engine: add setting shim dma bd operation

Add operation to set SHIM DMA buffer descriptor.

The following operations are added to set the buffer descriptors:
* attach DMA buffer which enables AI engine device to access the DMA
buffer memory
* detach DMA buffer which disables AI engine device to access the DMA
buffer memory
* set DMA buffer descriptor, which takes buffer descriptor contents
pointer, the dmabuf fd, and the offset to the start of dmabuf as
as argument. It validates the dmabuf and the offset and size of the
buffer. And then it calculates the DMA address of the buffer and set
the buffer descriptor content to the hardware DMA buffer descriptor.

The main logic to control what's go into the buffer descriptor and which
buffer descriptor to use is in the userspace AI engine library.

Signed-off-by: Wendy Liang <[email protected]>
Reviewed-by: Hyun Kwon <[email protected]>
---
drivers/misc/xilinx-ai-engine/Makefile | 1 +
drivers/misc/xilinx-ai-engine/ai-engine-aie.c | 19 +
drivers/misc/xilinx-ai-engine/ai-engine-dma.c | 481 +++++++++++++++++++++
drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 45 ++
drivers/misc/xilinx-ai-engine/ai-engine-part.c | 17 +
include/uapi/linux/xlnx-ai-engine.h | 43 ++
6 files changed, 606 insertions(+)
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dma.c

diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-engine/Makefile
index 2dbed42..1b743fa 100644
--- a/drivers/misc/xilinx-ai-engine/Makefile
+++ b/drivers/misc/xilinx-ai-engine/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_XILINX_AIE) += xilinx-aie.o

xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \
ai-engine-dev.o \
+ ai-engine-dma.o \
ai-engine-mem.o \
ai-engine-part.o \
ai-engine-res.o \
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
index 7fce2f00..19c262d 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
@@ -107,6 +107,24 @@ static const struct aie_single_reg_field aie_col_clkbuf = {
.regoff = AIE_SHIMPL_CLKCNTR_REGOFF,
};

+static const struct aie_dma_attr aie_shimdma = {
+ .laddr = {
+ .mask = 0xffffffffU,
+ .regoff = 0U,
+ },
+ .haddr = {
+ .mask = 0xffff0000U,
+ .regoff = 0x8U,
+ },
+ .buflen = {
+ .mask = 0xffffffffU,
+ .regoff = 0x4U,
+ },
+ .bd_regoff = 0x0001d000U,
+ .num_bds = 16,
+ .bd_len = 0x14U,
+};
+
static u32 aie_get_tile_type(struct aie_location *loc)
{
if (loc->row)
@@ -232,6 +250,7 @@ int aie_device_init(struct aie_device *adev)
adev->kernel_regs = aie_kernel_regs;
adev->col_rst = &aie_col_rst;
adev->col_clkbuf = &aie_col_clkbuf;
+ adev->shim_dma = &aiev1_shimdma;

/* Get the columns resource */
/* Get number of columns from AI engine memory resource */
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dma.c b/drivers/misc/xilinx-ai-engine/ai-engine-dma.c
new file mode 100644
index 0000000..007bec4
--- /dev/null
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-dma.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx AI Engine driver DMA implementation
+ *
+ * Copyright (C) 2020 Xilinx, Inc.
+ */
+
+#include "ai-engine-internal.h"
+#include <linux/dma-buf.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/refcount.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+/**
+ * struct aie_dmabuf - AI engine dmabuf information
+ * @attach: dmabuf attachment pointer
+ * @sgt: scatter/gather table
+ * @refs: refcount of the attached aie_dmabuf
+ * @node: list node
+ */
+struct aie_dmabuf {
+ struct dma_buf_attachment *attach;
+ struct sg_table *sgt;
+ refcount_t refs;
+ struct list_head node;
+};
+
+/**
+ * aie_part_find_dmabuf() - find a attached dmabuf
+ * @apart: AI engine partition
+ * @dmabuf: pointer to dmabuf
+ * @return: pointer to AI engine dmabuf struct of the found dmabuf, if dmabuf
+ * is not found, returns NULL.
+ *
+ * This function scans all the attached dmabufs to see the input dmabuf is
+ * in the list. if it is attached, return the corresponding struct aie_dmabuf
+ * pointer.
+ */
+static struct aie_dmabuf *
+aie_part_find_dmabuf(struct aie_partition *apart, struct dma_buf *dmabuf)
+{
+ struct aie_dmabuf *adbuf;
+
+ list_for_each_entry(adbuf, &apart->dbufs, node) {
+ if (dmabuf == adbuf->attach->dmabuf)
+ return adbuf;
+ }
+
+ return NULL;
+}
+
+/**
+ * aie_part_get_dmabuf_da_from_off() - get DMA address from offset to a dmabuf
+ * @apart: AI engine partition
+ * @dmabuf_fd: dmabuf file descriptor
+ * @off: offset to the start of a dmabuf
+ * @len: memory length
+ * @return: dma address, or 0 if @off or @len is invalid, or if @dmabuf_fd is
+ * not attached.
+ *
+ * This function returns DMA address if has been mapped to a dmabuf which has
+ * been attached to the AI engine partition.
+ */
+static dma_addr_t
+aie_part_get_dmabuf_da_from_off(struct aie_partition *apart, int dmabuf_fd,
+ u64 off, size_t len)
+{
+ struct dma_buf *dbuf = dma_buf_get(dmabuf_fd);
+ struct aie_dmabuf *adbuf;
+
+ if (IS_ERR(dbuf)) {
+ dev_err(&apart->dev,
+ "failed to get dma address, not able to get dmabuf from %d.\n",
+ dmabuf_fd);
+ return 0;
+ }
+
+ adbuf = aie_part_find_dmabuf(apart, dbuf);
+ dma_buf_put(dbuf);
+ if (!adbuf) {
+ dev_err(&apart->dev,
+ "failed to get dma address, dmabuf %d not attached.\n",
+ dmabuf_fd);
+ return 0;
+ }
+
+ if (off >= dbuf->size || off + len >= dbuf->size) {
+ dev_err(&apart->dev,
+ "failed to get dma address from buf %d, off=0x%llx, len=0x%zx.\n",
+ dmabuf_fd, off, len);
+ return 0;
+ }
+
+ return sg_dma_address(adbuf->sgt->sgl) + off;
+}
+
+/**
+ * aie_part_set_shimdma_bd() - Set the buffer descriptor to AI engine partition
+ * hardware
+ * @apart: AI engine partition
+ * @loc: AI engine tile location
+ * @bd_id: buffer descriptor ID
+ * @bd: pointer buffer descriptor content
+ * @return: 0 for success, negative value for failure
+ *
+ * This function sets the specified buffer descriptor content to the
+ * specified buffer descriptor in the specified AI engine SHIM NOC tile.
+ */
+static int aie_part_set_shimdma_bd(struct aie_partition *apart,
+ struct aie_location loc, u32 bd_id, u32 *bd)
+{
+ const struct aie_dma_attr *shim_dma = apart->adev->shim_dma;
+ struct aie_location loc_adjust;
+ u32 i, regoff, intile_regoff;
+
+ intile_regoff = shim_dma->bd_regoff + shim_dma->bd_len * bd_id;
+ loc_adjust.col = loc.col + apart->range.start.col;
+ loc_adjust.row = loc.row + apart->range.start.row;
+ regoff = aie_cal_regoff(apart->adev, loc_adjust, intile_regoff);
+
+ for (i = 0; i < shim_dma->bd_len / (sizeof(*bd));
+ i++, regoff += sizeof(*bd))
+ iowrite32(bd[i], apart->adev->base + regoff);
+ return 0;
+}
+
+/**
+ * aie_part_validate_bdloc() - Validate SHIM DMA buffer descriptor location
+ * @apart: AI engine partition
+ * @loc: tile location
+ * @bd_id: buffer descriptor id
+ *
+ * @return: 0 for success, negative value for failure
+ *
+ * This function validate the SHIM DMA buffer descriptor base address.
+ */
+static int aie_part_validate_bdloc(struct aie_partition *apart,
+ struct aie_location loc, u32 bd_id)
+{
+ const struct aie_dma_attr *shim_dma = apart->adev->shim_dma;
+ struct aie_location loc_adjust;
+ u32 ttype;
+
+ loc_adjust.col = loc.col + apart->range.start.col;
+ loc_adjust.row = loc.row + apart->range.start.row;
+
+ if (aie_validate_location(apart, loc_adjust) < 0) {
+ dev_err(&apart->dev,
+ "invalid loc (%u,%u) in (%u,%u).\n",
+ loc.col, loc.row,
+ apart->range.size.col, apart->range.size.row);
+ return -EINVAL;
+ }
+
+ ttype = apart->adev->ops->get_tile_type(&loc_adjust);
+ if (ttype != AIE_TILE_TYPE_SHIMNOC) {
+ dev_err(&apart->dev,
+ "failed to set bd, (%u,%u) is not SHIM NOC\n",
+ loc.col, loc.row);
+ return -EINVAL;
+ }
+
+ if (bd_id >= shim_dma->num_bds) {
+ dev_err(&apart->dev,
+ "invalid SHIM DMA bd id: %u.\n", bd_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * aie_part_attach_dmabuf() - Attach dmabuf to an AI engine
+ * @apart: AI engine partition
+ * @dbuf: pointer to the DMA buffer to attach
+ * @return: pointer to AI engine dmabuf structure for success, or error value
+ * for failure
+ *
+ * This function attaches a dmabuf to the specified AI engine partition.
+ */
+static struct aie_dmabuf *aie_part_attach_dmabuf(struct aie_partition *apart,
+ struct dma_buf *dbuf)
+{
+ struct aie_dmabuf *adbuf;
+ struct dma_buf_attachment *attach;
+ struct sg_table *sgt;
+
+ attach = dma_buf_attach(dbuf, &apart->dev);
+ if (IS_ERR(attach)) {
+ dev_err(&apart->dev, "failed to attach dmabuf\n");
+ return ERR_CAST(attach);
+ }
+
+ sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+ if (IS_ERR(sgt)) {
+ dev_err(&apart->dev, "failed to map dmabuf attachment\n");
+ dma_buf_detach(dbuf, attach);
+ return ERR_CAST(sgt);
+ }
+
+ if (sgt->nents != 1) {
+ dma_addr_t next_sg_addr = sg_dma_address(sgt->sgl);
+ struct scatterlist *s;
+ unsigned int i;
+
+ for_each_sg(sgt->sgl, s, sgt->nents, i) {
+ if (sg_dma_address(s) != next_sg_addr) {
+ dev_err(&apart->dev,
+ "dmabuf not contiguous\n");
+ dma_buf_unmap_attachment(attach, sgt,
+ attach->dir);
+ dma_buf_detach(dbuf, attach);
+ return ERR_PTR(-EINVAL);
+ }
+
+ next_sg_addr = sg_dma_address(s) + sg_dma_len(s);
+ }
+ }
+
+ adbuf = devm_kzalloc(&apart->dev, sizeof(*adbuf), GFP_KERNEL);
+ if (!adbuf) {
+ dma_buf_unmap_attachment(attach, sgt, attach->dir);
+ dma_buf_detach(dbuf, attach);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ adbuf->attach = attach;
+ /*
+ * dmabuf attachment doesn't always include the sgt, store it in
+ * AI engine dma buf structure.
+ */
+ adbuf->sgt = sgt;
+
+ refcount_set(&adbuf->refs, 1);
+
+ list_add(&adbuf->node, &apart->dbufs);
+ return adbuf;
+}
+
+/**
+ * aie_part_dmabuf_attach_get() - Get reference to an dmabuf attachment
+ * @adbuf: AI engine partition attached dmabuf
+ *
+ * This call will increase the reference count by 1
+ */
+static void aie_part_dmabuf_attach_get(struct aie_dmabuf *adbuf)
+{
+ refcount_inc(&adbuf->refs);
+}
+
+/**
+ * aie_part_dmabuf_attach_put() - Put reference to an dmabuf attachment
+ * @adbuf: AI engine partition attached dmabuf
+ *
+ * This call will decrease the reference count by 1. If the refcount reaches
+ * 0, it will detach the dmabuf.
+ */
+static void aie_part_dmabuf_attach_put(struct aie_dmabuf *adbuf)
+{
+ struct dma_buf *dbuf;
+
+ if (!refcount_dec_and_test(&adbuf->refs))
+ return;
+
+ dbuf = adbuf->attach->dmabuf;
+ dma_buf_unmap_attachment(adbuf->attach, adbuf->sgt, adbuf->attach->dir);
+ dma_buf_detach(dbuf, adbuf->attach);
+ dma_buf_put(dbuf);
+ list_del(&adbuf->node);
+}
+
+/**
+ * aie_part_release_dmabufs() - detach all the attached dmabufs from partition
+ * @apart: AI engine partition
+ */
+void aie_part_release_dmabufs(struct aie_partition *apart)
+{
+ struct aie_dmabuf *adbuf, *tmpadbuf;
+
+ list_for_each_entry_safe(adbuf, tmpadbuf, &apart->dbufs, node) {
+ struct dma_buf *dbuf = adbuf->attach->dmabuf;
+
+ dma_buf_unmap_attachment(adbuf->attach, adbuf->sgt,
+ adbuf->attach->dir);
+ dma_buf_detach(dbuf, adbuf->attach);
+ dma_buf_put(dbuf);
+ list_del(&adbuf->node);
+ devm_kfree(&apart->dev, adbuf);
+ }
+}
+
+/**
+ * aie_part_attach_dmabuf_req() - Handle attaching dmabuf to an AI engine
+ * partition request
+ * @apart: AI engine partition
+ * @user_args: user AI engine dmabuf argument
+ *
+ * @return: 0 for success, negative value for failure
+ *
+ * This function attaches a dmabuf to the specified AI engine partition and map
+ * the attachment. It checks if the dmabuf is already attached, if it is not
+ * attached, attach it. It returns the number of entries of the attachment to
+ * the AI engine dmabuf user argument. If user wants to know the sg list, it
+ * can use AI engine get sg ioctl.
+ */
+long aie_part_attach_dmabuf_req(struct aie_partition *apart,
+ void __user *user_args)
+{
+ struct aie_dmabuf *adbuf;
+ struct dma_buf *dbuf;
+ long ret;
+ int dmabuf_fd = (int)(uintptr_t)user_args;
+
+ dbuf = dma_buf_get(dmabuf_fd);
+ if (IS_ERR(dbuf)) {
+ dev_err(&apart->dev, "failed to get dmabuf from %d.\n",
+ dmabuf_fd);
+ return PTR_ERR(dbuf);
+ }
+
+ ret = mutex_lock_interruptible(&apart->mlock);
+ if (ret) {
+ dma_buf_put(dbuf);
+ return ret;
+ }
+
+ adbuf = aie_part_find_dmabuf(apart, dbuf);
+ if (!adbuf)
+ adbuf = aie_part_attach_dmabuf(apart, dbuf);
+ else
+ aie_part_dmabuf_attach_get(adbuf);
+
+ mutex_unlock(&apart->mlock);
+
+ if (IS_ERR(adbuf)) {
+ dev_err(&apart->dev, "failed to attach dmabuf\n");
+ dma_buf_put(dbuf);
+ return PTR_ERR(adbuf);
+ }
+
+ return 0;
+}
+
+/**
+ * aie_part_detach_dmabuf_req() - Handle detaching dmabuf from an AI engine
+ * partition request
+ * @apart: AI engine partition
+ * @user_args: user AI engine dmabuf argument
+ *
+ * @return: 0 for success, negative value for failure
+ *
+ * This function unmaps and detaches a dmabuf from the specified AI engine
+ * partition.
+ */
+long aie_part_detach_dmabuf_req(struct aie_partition *apart,
+ void __user *user_args)
+{
+ int dmabuf_fd;
+ struct dma_buf *dbuf;
+ struct aie_dmabuf *adbuf;
+ int ret;
+
+ dmabuf_fd = (int)(uintptr_t)user_args;
+
+ dbuf = dma_buf_get(dmabuf_fd);
+ if (IS_ERR(dbuf)) {
+ dev_err(&apart->dev, "failed to get dmabuf %d.\n", dmabuf_fd);
+ return PTR_ERR(dbuf);
+ }
+
+ ret = mutex_lock_interruptible(&apart->mlock);
+ if (ret) {
+ dma_buf_put(dbuf);
+ return ret;
+ }
+
+ adbuf = aie_part_find_dmabuf(apart, dbuf);
+ dma_buf_put(dbuf);
+ if (!adbuf) {
+ dev_err(&apart->dev, "failed to find dmabuf %d.\n", dmabuf_fd);
+ mutex_unlock(&apart->mlock);
+ return -EINVAL;
+ }
+
+ aie_part_dmabuf_attach_put(adbuf);
+
+ mutex_unlock(&apart->mlock);
+
+ return 0;
+}
+
+/**
+ * aie_part_set_dmabuf_bd() - Set AI engine SHIM DMA dmabuf buffer descriptor
+ * @apart: AI engine partition
+ * @user_args: user AI engine dmabuf argument
+ *
+ * @return: 0 for success, negative value for failure
+ *
+ * This function set the user specified buffer descriptor into the SHIM DMA
+ * buffer descriptor. The buffer descriptor contained in the @user_args has the
+ * offset to the start of the buffer descriptor.
+ */
+long aie_part_set_dmabuf_bd(struct aie_partition *apart,
+ void __user *user_args)
+{
+ struct aie_device *adev = apart->adev;
+ const struct aie_dma_attr *shim_dma = adev->shim_dma;
+ struct aie_dmabuf_bd_args args;
+ u32 *bd, *tmpbd, len, laddr, haddr, regval;
+ u64 off;
+ dma_addr_t addr;
+ int ret;
+
+ if (copy_from_user(&args, user_args, sizeof(args)))
+ return -EFAULT;
+
+ ret = aie_part_validate_bdloc(apart, args.loc, args.bd_id);
+ if (ret) {
+ dev_err(&apart->dev, "invalid SHIM DMA BD reg address.\n");
+ return -EINVAL;
+ }
+
+ bd = memdup_user((void __user *)args.bd, shim_dma->bd_len);
+ if (IS_ERR(bd))
+ return PTR_ERR(bd);
+
+ regval = bd[shim_dma->buflen.regoff / sizeof(u32)];
+ len = aie_get_reg_field(&shim_dma->buflen, regval);
+ if (!len) {
+ dev_err(&apart->dev, "no buf length from shim dma bd.\n");
+ kfree(bd);
+ return -EINVAL;
+ }
+
+ /* Get low 32bit address offset */
+ tmpbd = (u32 *)((char *)bd + shim_dma->laddr.regoff);
+ laddr = *tmpbd & shim_dma->laddr.mask;
+ /* Get high 32bit address offset */
+ tmpbd = (u32 *)((char *)bd + shim_dma->haddr.regoff);
+ haddr = *tmpbd & shim_dma->haddr.mask;
+ off = laddr | ((u64)haddr << 32);
+
+ ret = mutex_lock_interruptible(&apart->mlock);
+ if (ret) {
+ kfree(bd);
+ return ret;
+ }
+
+ /* Get device address from offset */
+ addr = aie_part_get_dmabuf_da_from_off(apart, args.buf_fd, off, len);
+ if (!addr) {
+ dev_err(&apart->dev, "invalid buffer 0x%llx, 0x%x.\n",
+ off, len);
+ mutex_unlock(&apart->mlock);
+ kfree(bd);
+ return -EINVAL;
+ }
+
+ /* Set low 32bit address */
+ laddr = lower_32_bits(addr);
+ tmpbd = (u32 *)((char *)bd + shim_dma->laddr.regoff);
+ *tmpbd &= ~shim_dma->laddr.mask;
+ *tmpbd |= aie_get_field_val(&shim_dma->laddr, laddr);
+
+ /* Set high 32bit address */
+ haddr = upper_32_bits(addr);
+ tmpbd = (u32 *)((char *)bd + shim_dma->haddr.regoff);
+ *tmpbd &= ~shim_dma->haddr.mask;
+ *tmpbd |= aie_get_field_val(&shim_dma->haddr, haddr);
+
+ ret = aie_part_set_shimdma_bd(apart, args.loc, args.bd_id, bd);
+ mutex_unlock(&apart->mlock);
+ if (ret)
+ dev_err(&apart->dev, "failed to set to shim dma bd.\n");
+
+ kfree(bd);
+ return ret;
+}
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
index e84610b..bf3a09c 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
@@ -90,6 +90,24 @@ struct aie_part_mem {
};

/**
+ * struct aie_dma_attr - AI engine DMA attributes structure
+ * @laddr: low address field attributes
+ * @haddr: high address field attributes
+ * @buflen: buffer length field attributes
+ * @bd_regoff: SHIM DMA buffer descriptors register offset
+ * @num_bds: number of buffer descriptors
+ * @bd_len: length of a buffer descriptor in bytes
+ */
+struct aie_dma_attr {
+ struct aie_single_reg_field laddr;
+ struct aie_single_reg_field haddr;
+ struct aie_single_reg_field buflen;
+ u32 bd_regoff;
+ u32 num_bds;
+ u32 bd_len;
+};
+
+/**
* struct aie_tile_operations - AI engine device operations
* @get_tile_type: get type of tile based on tile operation
* @get_mem_info: get different types of memories information
@@ -127,6 +145,7 @@ struct aie_resource {
* @ops: tile operations
* @col_rst: column reset attribute
* @col_clkbuf: column clock buffer attribute
+ * @shim_dma: SHIM DMA attribute
* @size: size of the AI engine address space
* @array_shift: array address shift
* @col_shift: column address shift
@@ -147,6 +166,7 @@ struct aie_device {
const struct aie_tile_operations *ops;
const struct aie_single_reg_field *col_rst;
const struct aie_single_reg_field *col_clkbuf;
+ const struct aie_dma_attr *shim_dma;
size_t size;
struct aie_resource cols_res;
u32 array_shift;
@@ -159,6 +179,7 @@ struct aie_device {
/**
* struct aie_partition - AI engine partition structure
* @node: list node
+ * @dbufs: dmabufs list
* @adev: pointer to AI device instance
* @pmems: pointer to partition memories types
* @range: range of partition
@@ -172,6 +193,7 @@ struct aie_device {
*/
struct aie_partition {
struct list_head node;
+ struct list_head dbufs;
struct aie_device *adev;
struct aie_part_mem *pmems;
struct aie_range range;
@@ -229,6 +251,20 @@ static inline u32 aie_get_field_val(const struct aie_single_reg_field *field,
}

/**
+ * aie_get_reg_field() - get value from a field from a register valuer
+ * @field: a field in a register
+ * @regval: register value
+ * @return: value of a register field
+ */
+static inline u32 aie_get_reg_field(const struct aie_single_reg_field *field,
+ u32 regval)
+{
+ long long mask64 = (long long)field->mask & 0x00000000ffffffff;
+
+ return (regval & field->mask) >> __bf_shf(mask64);
+}
+
+/**
* aie_cal_regoff() - calculate register offset to the whole AI engine
* device start address
* @adev: AI engine device
@@ -286,5 +322,14 @@ int aie_part_clean(struct aie_partition *apart);

int aie_mem_get_info(struct aie_partition *apart, unsigned long arg);

+long aie_part_attach_dmabuf_req(struct aie_partition *apart,
+ void __user *user_args);
+long aie_part_detach_dmabuf_req(struct aie_partition *apart,
+ void __user *user_args);
+long aie_part_set_bd(struct aie_partition *apart, void __user *user_args);
+long aie_part_set_dmabuf_bd(struct aie_partition *apart,
+ void __user *user_args);
+void aie_part_release_dmabufs(struct aie_partition *apart);
+
int aie_device_init(struct aie_device *adev);
#endif /* AIE_INTERNAL_H */
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
index 4be6d38..dcfb9ec 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-part.c
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
@@ -8,6 +8,7 @@
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/dma-mapping.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -221,6 +222,7 @@ static int aie_part_release(struct inode *inode, struct file *filp)
if (ret)
return ret;

+ aie_part_release_dmabufs(apart);
aie_part_clean(apart);

apart->status = 0;
@@ -296,6 +298,12 @@ static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
}
case AIE_GET_MEM_IOCTL:
return aie_mem_get_info(apart, arg);
+ case AIE_ATTACH_DMABUF_IOCTL:
+ return aie_part_attach_dmabuf_req(apart, argp);
+ case AIE_DETACH_DMABUF_IOCTL:
+ return aie_part_detach_dmabuf_req(apart, argp);
+ case AIE_SET_SHIMDMA_DMABUF_BD_IOCTL:
+ return aie_part_set_dmabuf_bd(apart, argp);
default:
dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd);
ret = -EINVAL;
@@ -422,6 +430,7 @@ static struct aie_partition *aie_create_partition(struct aie_device *adev,
return ERR_PTR(-ENOMEM);

apart->adev = adev;
+ INIT_LIST_HEAD(&apart->dbufs);
memcpy(&apart->range, range, sizeof(*range));
mutex_init(&apart->mlock);

@@ -443,6 +452,10 @@ static struct aie_partition *aie_create_partition(struct aie_device *adev,
return ERR_PTR(ret);
}

+ /* Set up the DMA mask */
+ dev->coherent_dma_mask = DMA_BIT_MASK(48);
+ dev->dma_mask = &dev->coherent_dma_mask;
+
/*
* Create array to keep the information of the different types of tile
* memories information of the AI engine partition.
@@ -521,6 +534,10 @@ of_aie_part_probe(struct aie_device *adev, struct device_node *nc)
apart->dev.of_node = nc;
apart->partition_id = partition_id;

+ ret = of_dma_configure(&apart->dev, nc, true);
+ if (ret)
+ dev_warn(&apart->dev, "Failed to configure DMA.\n");
+
dev_info(&adev->dev,
"AI engine part(%u,%u),(%u,%u), id %u is probed successfully.\n",
range.start.col, range.start.row,
diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-engine.h
index 5e40d00..9080f57 100644
--- a/include/uapi/linux/xlnx-ai-engine.h
+++ b/include/uapi/linux/xlnx-ai-engine.h
@@ -129,6 +129,21 @@ struct aie_partition_req {
__u32 flag;
};

+/**
+ * struct aie_dmabuf_bd_args - AIE dmabuf buffer descriptor information
+ * @bd: DMA buffer descriptor, within the buffer descriptor, the address field
+ * will be the offset to the start of the dmabuf
+ * @buf_fd: DMA buffer handler which is dmabuf file descriptor
+ * @loc: Tile location relative to the start of a partition
+ * @bd_id: buffer descriptor id
+ */
+struct aie_dmabuf_bd_args {
+ __u32 *bd;
+ struct aie_location loc;
+ int buf_fd;
+ __u32 bd_id;
+};
+
#define AIE_IOCTL_BASE 'A'

/* AI engine device IOCTL operations */
@@ -159,4 +174,32 @@ struct aie_partition_req {
*/
#define AIE_GET_MEM_IOCTL _IOWR(AIE_IOCTL_BASE, 0x9, \
struct aie_mem_args)
+/**
+ * DOC: AIE_ATTACH_DMABUF_IOCTL - attach a dmabuf to AI engine partition
+ *
+ * This ioctl is used to attach a dmabuf to the AI engine partition. AI engine
+ * partition will return the number of scatter gather list elements of the
+ * dmabuf.
+ */
+#define AIE_ATTACH_DMABUF_IOCTL _IOR(AIE_IOCTL_BASE, 0xa, int)
+
+/**
+ * DOC: AIE_DETACH_DMABUF_IOCTL - dettach a dmabuf from AI engine partition
+ *
+ * This ioctl is used to detach a dmabuf from the AI engine partition
+ */
+#define AIE_DETACH_DMABUF_IOCTL _IOR(AIE_IOCTL_BASE, 0xb, int)
+
+/**
+ * DOC: AIE_SET_SHIMDMA_DMABUF_BD_IOCTL - set buffer descriptor which contains
+ * dmabuf to SHIM DMA
+ *
+ * This ioctl is used to set the buffer descriptor to SHIM DMA. The
+ * aie_dmabuf_bd_args contains the dmabuf fd and the buffer descriptor contents.
+ * The address field in the buffer descriptor contents should be the offset to
+ * the start of the dmabuf.
+ */
+#define AIE_SET_SHIMDMA_DMABUF_BD_IOCTL _IOW(AIE_IOCTL_BASE, 0x10, \
+ struct aie_dmabuf_bd_args)
+
#endif
--
2.7.4

2020-11-18 08:10:43

by Wendy Liang

[permalink] [raw]
Subject: [PATCH 7/9] misc: xilinx-ai-engine: Add support to request device management services

From: Nishad Saraf <[email protected]>

Platform management services like device control, resets, power
management, etc. are provided by Platform, Loader and Manager(PLM)
through firmware driver APIs. For requesting some of these services,
this change reads AI Engine platform management node ID from DT node.
Some other features like clearing interrupts in the NoC interconnect
might only be valid for particular silicon revisions. For supporting
such silicon specific features, AI Engine driver will query and store
this information in device instance. While at it, this change makes
EEMI operations accessible to all the other source files in the
driver.

Signed-off-by: Nishad Saraf <[email protected]>
Signed-off-by: Wendy Liang <[email protected]>
---
drivers/misc/xilinx-ai-engine/ai-engine-dev.c | 25 +++++++++++++++++++++-
drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 6 ++++++
2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
index 7e69ff4..78eae90 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
@@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/file.h>
+#include <linux/firmware/xlnx-zynqmp.h>
#include <linux/fs.h>
#include <linux/idr.h>
#include <linux/list.h>
@@ -25,7 +26,8 @@

#include "ai-engine-internal.h"

-#define AIE_DEV_MAX (MINORMASK + 1)
+#define AIE_DEV_MAX (MINORMASK + 1)
+#define VERSAL_SILICON_REV_MASK GENMASK(31, 28)

static dev_t aie_major;
struct class *aie_class;
@@ -318,6 +320,7 @@ static int xilinx_ai_engine_probe(struct platform_device *pdev)
{
struct aie_device *adev;
struct device *dev;
+ u32 idcode, version, pm_reg[2];
int ret;

adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
@@ -345,6 +348,26 @@ static int xilinx_ai_engine_probe(struct platform_device *pdev)
return ret;
}

+ /*
+ * AI Engine platform management node ID is required for requesting
+ * services from firmware driver.
+ */
+ ret = of_property_read_u32_array(pdev->dev.of_node, "power-domains",
+ pm_reg, ARRAY_SIZE(pm_reg));
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Failed to read power management information\n");
+ return ret;
+ }
+ adev->pm_node_id = pm_reg[1];
+
+ ret = zynqmp_pm_get_chipid(&idcode, &version);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to get chip ID\n");
+ return ret;
+ }
+ adev->version = FIELD_GET(VERSAL_SILICON_REV_MASK, idcode);
+
dev = &adev->dev;
device_initialize(dev);
dev->class = aie_class;
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
index 131d22a..b21b7025 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
@@ -41,6 +41,10 @@
#define AIE_REGS_ATTR_PERM_MASK GENMASK(15, \
AIE_REGS_ATTR_PERM_SHIFT)

+/* Silicon Engineering Sample(ES) revision ID */
+#define VERSAL_ES1_REV_ID 0x0
+#define VERSAL_ES2_REV_ID 0x1
+
/**
* struct aie_tile_regs - contiguous range of AI engine register
* within an AI engine tile
@@ -173,6 +177,7 @@ struct aie_resource {
* while columns are occupied by partitions.
* @num_kernel_regs: number of kernel only registers range
* @version: AI engine device version
+ * @pm_node_id: AI Engine platform management node ID
*/
struct aie_device {
struct list_head partitions;
@@ -193,6 +198,7 @@ struct aie_device {
u32 row_shift;
u32 num_kernel_regs;
int version;
+ u32 pm_node_id;
};

/**
--
2.7.4

2020-11-18 08:10:50

by Wendy Liang

[permalink] [raw]
Subject: [PATCH 8/9] firmware: xilinx: Add IOCTL support for AIE ISR Clear

From: Izhar Ameer Shaikh <[email protected]>

Latching of AIE NPI Interrupts is present in Versal ES1 Silicon Rev,
however it has been removed from ES2 rev.
As a result on ES1, in order to use the interrupt, a client needs to
request PMC to clear/ack the interrupt.

Provide an EEMI IOCTL to serve the same purpose. Note that, this will
only be applicable for ES1 rev. For ES2 and other non-silicon platforms,
this call will essentially be a NOP in the firmware.

Signed-off-by: Izhar Ameer Shaikh <[email protected]>
Signed-off-by: Wendy Liang <[email protected]>
---
drivers/firmware/xilinx/zynqmp.c | 14 ++++++++++++++
include/linux/firmware/xlnx-zynqmp.h | 8 ++++++++
2 files changed, 22 insertions(+)

diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
index efb8a66..7a0c6a3 100644
--- a/drivers/firmware/xilinx/zynqmp.c
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -702,6 +702,20 @@ int zynqmp_pm_set_boot_health_status(u32 value)
}

/**
+ * zynqmp_pm_clear_aie_npi_isr - Clear AI engine NPI interrupt status register
+ * @node: AI engine node id
+ * @irq_mask: Mask of AI engine NPI interrupt bit to clear
+ *
+ * Return: Returns status, either success or error+reason
+ */
+int zynqmp_pm_clear_aie_npi_isr(u32 node, u32 irq_mask)
+{
+ return zynqmp_pm_invoke_fn(PM_IOCTL, node, IOCTL_AIE_ISR_CLEAR,
+ irq_mask, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_clear_aie_npi_isr);
+
+/**
* zynqmp_pm_reset_assert - Request setting of reset (1 - assert, 0 - release)
* @reset: Reset to be configured
* @assert_flag: Flag stating should reset be asserted (1) or
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
index 5968df8..b929d57 100644
--- a/include/linux/firmware/xlnx-zynqmp.h
+++ b/include/linux/firmware/xlnx-zynqmp.h
@@ -116,6 +116,8 @@ enum pm_ioctl_id {
IOCTL_READ_PGGS = 15,
/* Set healthy bit value */
IOCTL_SET_BOOT_HEALTH_STATUS = 17,
+ /* AI engine NPI ISR clear */
+ IOCTL_AIE_ISR_CLEAR = 24,
};

enum pm_query_id {
@@ -357,6 +359,7 @@ int zynqmp_pm_write_pggs(u32 index, u32 value);
int zynqmp_pm_read_pggs(u32 index, u32 *value);
int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype);
int zynqmp_pm_set_boot_health_status(u32 value);
+int zynqmp_pm_clear_aie_npi_isr(u32 node, u32 irq_mask);
#else
static inline struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void)
{
@@ -507,6 +510,11 @@ static inline int zynqmp_pm_set_boot_health_status(u32 value)
{
return -ENODEV;
}
+
+static int zynqmp_pm_clear_aie_npi_isr(u32 node, u32 irq_mask)
+{
+ return -ENODEV;
+}
#endif

#endif /* __FIRMWARE_ZYNQMP_H__ */
--
2.7.4

2020-11-18 08:11:11

by Wendy Liang

[permalink] [raw]
Subject: [PATCH 2/9] misc: Add Xilinx AI engine device driver

Create AI engine device/partition hierarchical structure.

Each AI engine device can have multiple logical partitions(groups of AI
engine tiles). Each partition is column based and has its own node ID
in the system. AI engine device driver manages its partitions.

Applications can access AI engine partition through the AI engine
partition driver instance. AI engine registers write is moved to kernel
as there are registers in the AI engine array needs privilege
permission.

Signed-off-by: Wendy Liang <[email protected]>
Signed-off-by: Hyun Kwon <[email protected]>
---
MAINTAINERS | 8 +
drivers/misc/Kconfig | 12 +
drivers/misc/Makefile | 1 +
drivers/misc/xilinx-ai-engine/Makefile | 11 +
drivers/misc/xilinx-ai-engine/ai-engine-aie.c | 115 +++++
drivers/misc/xilinx-ai-engine/ai-engine-dev.c | 448 ++++++++++++++++++
drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 226 ++++++++++
drivers/misc/xilinx-ai-engine/ai-engine-part.c | 498 +++++++++++++++++++++
drivers/misc/xilinx-ai-engine/ai-engine-res.c | 114 +++++
include/uapi/linux/xlnx-ai-engine.h | 107 +++++
10 files changed, 1540 insertions(+)
create mode 100644 drivers/misc/xilinx-ai-engine/Makefile
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-aie.c
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dev.c
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-internal.h
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-part.c
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-res.c
create mode 100644 include/uapi/linux/xlnx-ai-engine.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 5cc595a..40e3351 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19283,6 +19283,14 @@ T: git https://github.com/Xilinx/linux-xlnx.git
F: Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml
F: drivers/phy/xilinx/phy-zynqmp.c

+XILINX AI ENGINE DRIVER
+M: Wendy Liang <[email protected]>
+S: Supported
+F: Documentation/devicetree/bindings/soc/xilinx/xlnx,ai-engine.yaml
+F: drivers/misc/xilinx-ai-engine/
+F: include/linux/xlnx-ai-engine.h
+F: include/uapi/linux/xlnx-ai-engine.h
+
XILLYBUS DRIVER
M: Eli Billauer <[email protected]>
L: [email protected]
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index fafa8b0..0b8ce4d 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -444,6 +444,18 @@ config XILINX_SDFEC

If unsure, say N.

+config XILINX_AIE
+ tristate "Xilinx AI engine"
+ depends on ARM64 || COMPILE_TEST
+ help
+ This option enables support for the Xilinx AI engine driver.
+ One Xilinx AI engine device can have multiple partitions (groups of
+ AI engine tiles). Xilinx AI engine device driver instance manages
+ AI engine partitions. User application access its partitions through
+ AI engine partition instance file operations.
+
+ If unsure, say N
+
config MISC_RTSX
tristate
default MISC_RTSX_PCI || MISC_RTSX_USB
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index d23231e..2176b18 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_HABANA_AI) += habanalabs/
obj-$(CONFIG_UACCE) += uacce/
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
+obj-$(CONFIG_XILINX_AIE) += xilinx-ai-engine/
diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-engine/Makefile
new file mode 100644
index 0000000..7827a0a
--- /dev/null
+++ b/drivers/misc/xilinx-ai-engine/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for Xilinx AI engine device driver
+#
+
+obj-$(CONFIG_XILINX_AIE) += xilinx-aie.o
+
+xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \
+ ai-engine-dev.o \
+ ai-engine-part.o \
+ ai-engine-res.o
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
new file mode 100644
index 0000000..319260f
--- /dev/null
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx AI Engine driver AIE device specific implementation
+ *
+ * Copyright (C) 2020 Xilinx, Inc.
+ */
+
+#include <linux/slab.h>
+
+#include "ai-engine-internal.h"
+
+#define AIE_ARRAY_SHIFT 30U
+#define AIE_COL_SHIFT 23U
+#define AIE_ROW_SHIFT 18U
+
+/*
+ * Registers offsets
+ */
+#define AIE_SHIMNOC_L2INTR_MASK_REGOFF 0x00015000U
+#define AIE_SHIMNOC_L2INTR_INTR_REGOFF 0x00015010U
+#define AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF 0x0001d000U
+#define AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF 0x0001d13cU
+#define AIE_SHIMNOC_AXIMM_REGOFF 0x0001e020U
+#define AIE_SHIMPL_L1INTR_MASK_A_REGOFF 0x00035000U
+#define AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF 0x00035050U
+#define AIE_SHIMPL_CLKCNTR_REGOFF 0x00036040U
+#define AIE_SHIMPL_RESET_REGOFF 0x0003604cU
+#define AIE_TILE_CORE_CLKCNTR_REGOFF 0x00036040U
+
+static const struct aie_tile_regs aie_kernel_regs[] = {
+ /* SHIM AXI MM Config */
+ {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
+ .soff = AIE_SHIMNOC_AXIMM_REGOFF,
+ .eoff = AIE_SHIMNOC_AXIMM_REGOFF,
+ },
+ /* SHIM DMA ADDRESS range */
+ {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
+ .soff = AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF,
+ .eoff = AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF,
+ },
+ /* SHIM 2nd level interrupt controller */
+ {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
+ .soff = AIE_SHIMNOC_L2INTR_MASK_REGOFF,
+ .eoff = AIE_SHIMNOC_L2INTR_INTR_REGOFF,
+ },
+ /* SHIM 1st level interrupt controller */
+ {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
+ AIE_REGS_ATTR_TILE_TYPE_SHIFT,
+ .soff = AIE_SHIMPL_L1INTR_MASK_A_REGOFF,
+ .eoff = AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF,
+ },
+ /* SHIM reset Enable */
+ {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
+ AIE_REGS_ATTR_TILE_TYPE_SHIFT,
+ .soff = AIE_SHIMPL_RESET_REGOFF,
+ .eoff = AIE_SHIMPL_RESET_REGOFF,
+ },
+ /* SHIM clock control */
+ {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
+ AIE_REGS_ATTR_TILE_TYPE_SHIFT,
+ .soff = AIE_SHIMPL_CLKCNTR_REGOFF,
+ .eoff = AIE_SHIMPL_CLKCNTR_REGOFF,
+ },
+ /* Tile clock control */
+ {.attribute = AIE_TILE_TYPE_TILE << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
+ .soff = AIE_TILE_CORE_CLKCNTR_REGOFF,
+ .eoff = AIE_TILE_CORE_CLKCNTR_REGOFF,
+ },
+};
+
+static u32 aie_get_tile_type(struct aie_location *loc)
+{
+ if (loc->row)
+ return AIE_TILE_TYPE_TILE;
+ /* SHIM row */
+ if ((loc->col % 4) < 2)
+ return AIE_TILE_TYPE_SHIMPL;
+
+ return AIE_TILE_TYPE_SHIMNOC;
+}
+
+static const struct aie_tile_operations aie_ops = {
+ .get_tile_type = aie_get_tile_type,
+};
+
+/**
+ * aie_device_init() - Initialize AI engine device struct AIE specific
+ * properties
+ * @adev: AI engine device
+ * @return: 0 for success, negative value for failure.
+ *
+ * This function initialize the AI engine device structure device version
+ * specific elements such as register addressing related array shift,
+ * column shift, and row shift; AIE specific device operations, device
+ * columns resource.
+ */
+int aie_device_init(struct aie_device *adev)
+{
+ int ret;
+
+ adev->array_shift = AIE_ARRAY_SHIFT;
+ adev->col_shift = AIE_COL_SHIFT;
+ adev->row_shift = AIE_ROW_SHIFT;
+ adev->ops = &aie_ops;
+ adev->num_kernel_regs = ARRAY_SIZE(aie_kernel_regs);
+ adev->kernel_regs = aie_kernel_regs;
+
+ /* Get the columns resource */
+ /* Get number of columns from AI engine memory resource */
+ ret = aie_resource_initialize(&adev->cols_res, 50);
+ if (ret)
+ dev_err(&adev->dev, "failed to initialize columns resource.\n");
+
+ return ret;
+}
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
new file mode 100644
index 0000000..2ab2dc8
--- /dev/null
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx AI Engine device driver
+ *
+ * Copyright (C) 2020 Xilinx, Inc.
+ */
+
+#include <linux/anon_inodes.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/xlnx-ai-engine.h>
+
+#include "ai-engine-internal.h"
+
+#define AIE_DEV_MAX (MINORMASK + 1)
+
+static dev_t aie_major;
+struct class *aie_class;
+
+static DEFINE_IDA(aie_device_ida);
+static DEFINE_IDA(aie_minor_ida);
+
+/**
+ * aie_get_partition_fd() - Get AI engine partition file descriptor
+ * @apart: AI engine partition
+ * @return: file descriptor for AI engine partition for success, or negative
+ * value for failure.
+ *
+ * This function gets a file descriptor for the AI engine partition.
+ */
+static int aie_get_partition_fd(struct aie_partition *apart)
+{
+ struct file *filep;
+ int ret;
+
+ /*
+ * We can't use anon_inode_getfd() because we need to modify
+ * the f_mode flags directly to allow more than just ioctls
+ */
+ ret = get_unused_fd_flags(O_CLOEXEC);
+ if (ret < 0)
+ return ret;
+
+ filep = anon_inode_getfile(dev_name(&apart->dev), &aie_part_fops,
+ apart, O_RDWR);
+ if (IS_ERR(filep)) {
+ put_unused_fd(ret);
+ ret = PTR_ERR(filep);
+ return ret;
+ }
+ filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+ fd_install(ret, filep);
+
+ return ret;
+}
+
+/**
+ * aie_enquire_partitions() - get AI engine partitions information
+ * @adev: AI engine device
+ * @query: data struct to store the partition information
+ * @return: 0 for success, and negative value for failure.
+ */
+static int aie_enquire_partitions(struct aie_device *adev,
+ struct aie_partition_query *query)
+{
+ struct aie_partition *apart;
+ u32 partition_cnt, i = 0;
+ int ret;
+
+ if (!query->partitions) {
+ /*
+ * If partitions information buffer is NULL.
+ * It is to get the number of partitions.
+ */
+ query->partition_cnt = 0;
+ list_for_each_entry(apart, &adev->partitions, node)
+ query->partition_cnt++;
+ return 0;
+ }
+
+ partition_cnt = query->partition_cnt;
+ if (!partition_cnt)
+ return 0;
+
+ ret = mutex_lock_interruptible(&adev->mlock);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(apart, &adev->partitions, node) {
+ struct aie_range_args part;
+
+ if (i >= partition_cnt)
+ break;
+ part.partition_id = apart->partition_id;
+ /*
+ * TBD: check with PLM that if the partition is programmed
+ * and get the UID of the image which is loaded on the AI
+ * engine partition.
+ */
+ part.uid = 0;
+ part.range.start.col = apart->range.start.col;
+ part.range.start.row = apart->range.start.row;
+ part.range.size.col = apart->range.size.col;
+ part.range.size.row = apart->range.size.row;
+ /* Check if partition is in use */
+ part.status = apart->status;
+ if (copy_to_user((void __user *)&query->partitions[i], &part,
+ sizeof(part))) {
+ mutex_unlock(&adev->mlock);
+ return -EFAULT;
+ }
+ i++;
+ }
+ mutex_unlock(&adev->mlock);
+ query->partition_cnt = i;
+
+ return 0;
+}
+
+/**
+ * aie_get_partition_from_id() - get AI engine partition from id
+ * @adev: AI engine device
+ * @partition_id: partition id to check
+ * @return: partition pointer if partition exists, otherwise, NULL.
+ *
+ * This function checks defined partitions with partition id.
+ * This function expect the caller to lock mlock of @adev.
+ */
+struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
+ u32 partition_id)
+{
+ struct aie_partition *apart;
+
+ list_for_each_entry(apart, &adev->partitions, node) {
+ if (apart->partition_id == partition_id)
+ return apart;
+ }
+
+ return NULL;
+}
+
+/**
+ * aie_request_partition() - request AI engine partition
+ * @adev: AI engine device
+ * @req: partition request, includes the requested AI engine information
+ * such as partition node ID and the UID of the image which is
+ * loaded on the partition.
+ * @return: partition pointer if partition exists, otherwise, NULL.
+ *
+ * This function finds a defined partition which matches the specified
+ * partition id, request it if it hasn't been requested, and returns it.
+ */
+struct aie_partition *aie_request_partition(struct aie_device *adev,
+ struct aie_partition_req *req)
+{
+ struct aie_partition *apart;
+ int ret;
+
+ ret = mutex_lock_interruptible(&adev->mlock);
+ if (ret)
+ return ERR_PTR(ret);
+
+ apart = aie_get_partition_from_id(adev, req->partition_id);
+ if (!apart) {
+ dev_err(&adev->dev,
+ "request partition %u failed, not exist.\n",
+ req->partition_id);
+ mutex_unlock(&adev->mlock);
+ return ERR_PTR(-EINVAL);
+ }
+ /*
+ * TBD: It will check image UID too to see if the user matches
+ * what's loaded in the AI engine partition. And check the meta
+ * data to see which resources used by application.
+ */
+
+ ret = mutex_lock_interruptible(&apart->mlock);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (apart->status & XAIE_PART_STATUS_INUSE) {
+ mutex_unlock(&apart->mlock);
+ dev_err(&adev->dev,
+ "request partition %u failed, partition in use.\n",
+ req->partition_id);
+ apart = ERR_PTR(-EBUSY);
+ } else {
+ /*
+ * TBD:
+ * 1. setup NOC AXI MM config to only generate error events
+ * for slave error and decode error.
+ * 2. scan to see which tiles have been clock gated.
+ *
+ * This needs to be done before the AI engine partition is
+ * exported for user to access.
+ */
+ apart->status = XAIE_PART_STATUS_INUSE;
+ mutex_unlock(&apart->mlock);
+ }
+ mutex_unlock(&adev->mlock);
+
+ return apart;
+}
+
+static long xilinx_ai_engine_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct inode *inode = file_inode(filp);
+ struct aie_device *adev = cdev_to_aiedev(inode->i_cdev);
+ void __user *argp = (void __user *)arg;
+ int ret;
+
+ switch (cmd) {
+ case AIE_ENQUIRE_PART_IOCTL:
+ {
+ struct aie_partition_query query;
+ struct aie_partition_query __user *uquery_ptr = argp;
+
+ if (copy_from_user(&query, uquery_ptr, sizeof(query)))
+ return -EFAULT;
+ ret = aie_enquire_partitions(adev, &query);
+ if (ret < 0)
+ return ret;
+ if (copy_to_user((void __user *)&uquery_ptr->partition_cnt,
+ &query.partition_cnt,
+ sizeof(query.partition_cnt)))
+ return -EFAULT;
+ break;
+ }
+ case AIE_REQUEST_PART_IOCTL:
+ {
+ struct aie_partition_req req;
+ struct aie_partition *apart;
+
+ if (copy_from_user(&req, argp, sizeof(req)))
+ return -EFAULT;
+ apart = aie_request_partition(adev, &req);
+ if (IS_ERR(apart))
+ return PTR_ERR(apart);
+ ret = aie_get_partition_fd(apart);
+ if (ret < 0) {
+ dev_err(&apart->dev, "failed to get fd.\n");
+ break;
+ }
+ break;
+ }
+ default:
+ dev_err(&adev->dev, "Invalid ioctl command %u.\n", cmd);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct file_operations aie_device_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = xilinx_ai_engine_ioctl,
+};
+
+static void xilinx_ai_engine_release_device(struct device *dev)
+{
+ struct aie_device *adev = dev_to_aiedev(dev);
+
+ ida_simple_remove(&aie_device_ida, dev->id);
+ ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
+ cdev_del(&adev->cdev);
+ aie_resource_uninitialize(&adev->cols_res);
+}
+
+/**
+ * of_xilinx_ai_engine_part_probe() - probes for AI engine partition nodes
+ * @adev: AI engine device
+ *
+ * This function will probe for children AI engine partition nodes and create
+ * an AI engine partition instance for each node.
+ */
+static void of_xilinx_ai_engine_part_probe(struct aie_device *adev)
+{
+ struct device_node *nc;
+
+ for_each_available_child_of_node(adev->dev.of_node, nc) {
+ struct aie_partition *apart;
+
+ if (of_node_test_and_set_flag(nc, OF_POPULATED))
+ continue;
+ apart = of_aie_part_probe(adev, nc);
+ if (IS_ERR(apart)) {
+ dev_err(&adev->dev,
+ "Failed to probe AI engine part for %pOF\n",
+ nc);
+ of_node_clear_flag(nc, OF_POPULATED);
+ }
+ }
+}
+
+static int xilinx_ai_engine_probe(struct platform_device *pdev)
+{
+ struct aie_device *adev;
+ struct device *dev;
+ int ret;
+
+ adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, adev);
+ INIT_LIST_HEAD(&adev->partitions);
+ mutex_init(&adev->mlock);
+
+ adev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!adev->res) {
+ dev_err(&pdev->dev, "No memory resource.\n");
+ return -EINVAL;
+ }
+ adev->base = devm_ioremap_resource(&pdev->dev, adev->res);
+ if (IS_ERR(adev->base)) {
+ dev_err(&pdev->dev, "no io memory resource.\n");
+ return PTR_ERR(adev->base);
+ }
+
+ /* Initialize AIE device specific instance. */
+ ret = aie_device_init(adev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to initialize device instance.\n");
+ return ret;
+ }
+
+ dev = &adev->dev;
+ device_initialize(dev);
+ dev->class = aie_class;
+ dev->parent = &pdev->dev;
+ dev->of_node = pdev->dev.of_node;
+
+ ret = ida_simple_get(&aie_minor_ida, 0, AIE_DEV_MAX, GFP_KERNEL);
+ if (ret < 0)
+ goto free_dev;
+ dev->devt = MKDEV(MAJOR(aie_major), ret);
+ ret = ida_simple_get(&aie_device_ida, 0, 0, GFP_KERNEL);
+ if (ret < 0)
+ goto free_minor_ida;
+ dev->id = ret;
+ dev_set_name(&adev->dev, "aie%d", dev->id);
+
+ cdev_init(&adev->cdev, &aie_device_fops);
+ adev->cdev.owner = THIS_MODULE;
+ ret = cdev_add(&adev->cdev, dev->devt, 1);
+ if (ret)
+ goto free_ida;
+ /* We can now rely on the release function for cleanup */
+ dev->release = xilinx_ai_engine_release_device;
+
+ ret = device_add(dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device_add failed: %d\n", ret);
+ put_device(dev);
+ return ret;
+ }
+
+ of_xilinx_ai_engine_part_probe(adev);
+ dev_info(&pdev->dev, "Xilinx AI Engine device(cols=%u) probed\n",
+ adev->cols_res.total);
+ return 0;
+
+free_ida:
+ ida_simple_remove(&aie_device_ida, dev->id);
+free_minor_ida:
+ ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
+free_dev:
+ put_device(dev);
+
+ return ret;
+}
+
+static int xilinx_ai_engine_remove(struct platform_device *pdev)
+{
+ struct aie_device *adev = platform_get_drvdata(pdev);
+ struct aie_partition *apart;
+
+ list_for_each_entry(apart, &adev->partitions, node)
+ aie_part_remove(apart);
+
+ device_del(&adev->dev);
+ put_device(&adev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id xilinx_ai_engine_of_match[] = {
+ { .compatible = "xlnx,ai-engine-v1.0", },
+ { /* end of table */ },
+};
+MODULE_DEVICE_TABLE(of, xilinx_ai_engine_of_match);
+
+static struct platform_driver xilinx_ai_engine_driver = {
+ .probe = xilinx_ai_engine_probe,
+ .remove = xilinx_ai_engine_remove,
+ .driver = {
+ .name = "xilinx-ai-engine",
+ .of_match_table = xilinx_ai_engine_of_match,
+ },
+};
+
+static int __init xilinx_ai_engine_init(void)
+{
+ int ret;
+
+ ret = alloc_chrdev_region(&aie_major, 0, AIE_DEV_MAX, "aie");
+ if (ret < 0) {
+ pr_err("aie: failed to allocate aie region\n");
+ return ret;
+ }
+
+ aie_class = class_create(THIS_MODULE, "aie");
+ if (IS_ERR(aie_class)) {
+ pr_err("failed to create aie class\n");
+ unregister_chrdev_region(aie_major, AIE_DEV_MAX);
+ return PTR_ERR(aie_class);
+ }
+
+ platform_driver_register(&xilinx_ai_engine_driver);
+
+ return 0;
+}
+postcore_initcall(xilinx_ai_engine_init);
+
+static void __exit xilinx_ai_engine_exit(void)
+{
+ platform_driver_unregister(&xilinx_ai_engine_driver);
+ class_destroy(aie_class);
+ unregister_chrdev_region(aie_major, AIE_DEV_MAX);
+}
+module_exit(xilinx_ai_engine_exit);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
new file mode 100644
index 0000000..6a69946
--- /dev/null
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
@@ -0,0 +1,226 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Xilinx AI Engine driver internal header
+ *
+ * Copyright (C) 2020 Xilinx, Inc.
+ */
+
+#ifndef AIE_INTERNAL_H
+#define AIE_INTERNAL_H
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <uapi/linux/xlnx-ai-engine.h>
+
+/*
+ * Macros for AI engine tile type bitmasks
+ */
+#define AIE_TILE_TYPE_TILE BIT(0)
+#define AIE_TILE_TYPE_SHIMPL BIT(1)
+/* SHIM NOC tile includes SHIM PL and SHIM NOC modules */
+#define AIE_TILE_TYPE_SHIMNOC BIT(2)
+
+/*
+ * Macros for attribute property of AI engine registers accessed by kernel
+ * 0 - 7 bits: tile type bits
+ * 8 - 15 bits: permission bits. If it is 1, it allows write from userspace
+ */
+#define AIE_REGS_ATTR_TILE_TYPE_SHIFT 0U
+#define AIE_REGS_ATTR_PERM_SHIFT 8U
+#define AIE_REGS_ATTR_TILE_TYPE_MASK GENMASK(AIE_REGS_ATTR_PERM_SHIFT - 1, \
+ AIE_REGS_ATTR_TILE_TYPE_SHIFT)
+#define AIE_REGS_ATTR_PERM_MASK GENMASK(15, \
+ AIE_REGS_ATTR_PERM_SHIFT)
+
+/**
+ * struct aie_tile_regs - contiguous range of AI engine register
+ * within an AI engine tile
+ * @soff: start offset of the range
+ * @eoff: end offset of the range
+ * @attribute: registers attribute. It uses AIE_REGS_ATTR_* macros defined
+ * above.
+ */
+struct aie_tile_regs {
+ size_t soff;
+ size_t eoff;
+ u32 attribute;
+};
+
+struct aie_device;
+struct aie_partition;
+
+/**
+ * struct aie_tile_operations - AI engine device operations
+ * @get_tile_type: get type of tile based on tile operation
+ *
+ * Different AI engine device version has its own device
+ * operation.
+ */
+struct aie_tile_operations {
+ u32 (*get_tile_type)(struct aie_location *loc);
+};
+
+/**
+ * struct aie_resource - AI engine resource structure
+ * @bitmap: resource bitmap
+ * @total: total number of resource
+ */
+struct aie_resource {
+ unsigned long *bitmap;
+ u32 total;
+};
+
+/**
+ * struct aie_device - AI engine device structure
+ * @partitions: list of partitions requested
+ * @cdev: cdev for the AI engine
+ * @dev: device for the AI engine device
+ * @mlock: protection for AI engine device operations
+ * @base: AI engine device base virtual address
+ * @res: memory resource of AI engine device
+ * @kernel_regs: array of kernel only registers
+ * @ops: tile operations
+ * @size: size of the AI engine address space
+ * @array_shift: array address shift
+ * @col_shift: column address shift
+ * @row_shift: row address shift
+ * @cols_res: AI engine columns resources to indicate
+ * while columns are occupied by partitions.
+ * @num_kernel_regs: number of kernel only registers range
+ * @version: AI engine device version
+ */
+struct aie_device {
+ struct list_head partitions;
+ struct cdev cdev;
+ struct device dev;
+ struct mutex mlock; /* protection for AI engine partitions */
+ void __iomem *base;
+ struct resource *res;
+ const struct aie_tile_regs *kernel_regs;
+ const struct aie_tile_operations *ops;
+ size_t size;
+ struct aie_resource cols_res;
+ u32 array_shift;
+ u32 col_shift;
+ u32 row_shift;
+ u32 num_kernel_regs;
+ int version;
+};
+
+/**
+ * struct aie_partition - AI engine partition structure
+ * @node: list node
+ * @adev: pointer to AI device instance
+ * @range: range of partition
+ * @mlock: protection for AI engine partition operations
+ * @dev: device for the AI engine partition
+ * @partition_id: partition id. Partition ID is the identifier
+ * of the AI engine partition in the system.
+ * @status: indicate if the partition is in use
+ */
+struct aie_partition {
+ struct list_head node;
+ struct aie_device *adev;
+ struct aie_range range;
+ struct mutex mlock; /* protection for AI engine partition operations */
+ struct device dev;
+ u32 partition_id;
+ u32 status;
+};
+
+extern struct class *aie_class;
+extern const struct file_operations aie_part_fops;
+
+#define cdev_to_aiedev(i_cdev) container_of((i_cdev), struct aie_device, cdev)
+#define dev_to_aiedev(_dev) container_of((_dev), struct aie_device, dev)
+#define dev_to_aiepart(_dev) container_of((_dev), struct aie_partition, dev)
+
+#define aie_col_mask(adev) ({ \
+ struct aie_device *_adev = (adev); \
+ GENMASK_ULL(_adev->array_shift - 1, _adev->col_shift); \
+ })
+
+#define aie_row_mask(adev) ({ \
+ struct aie_device *_adev = (adev); \
+ GENMASK_ULL(_adev->col_shift - 1, _adev->row_shift); \
+ })
+
+#define aie_tile_reg_mask(adev) ({ \
+ struct aie_device *_adev = (adev); \
+ GENMASK_ULL(_adev->row_shift - 1, 0); \
+ })
+
+/*
+ * Need to define field get, as AI engine shift mask is not constant.
+ * Cannot use FIELD_GET()
+ */
+#define aie_tile_reg_field_get(mask, shift, regoff) ( \
+ ((regoff) & (mask)) >> (shift))
+
+#define aie_cal_tile_reg(adev, regoff) ( \
+ aie_tile_reg_field_get(aie_tile_reg_mask(adev), 0, regoff))
+
+/**
+ * aie_cal_regoff() - calculate register offset to the whole AI engine
+ * device start address
+ * @adev: AI engine device
+ * @loc: AI engine tile location
+ * @regoff_intile: register offset within a tile
+ * @return: register offset to the whole AI engine device start address
+ */
+static inline u32 aie_cal_regoff(struct aie_device *adev,
+ struct aie_location loc, u32 regoff_intile)
+{
+ return regoff_intile + (loc.col << adev->col_shift) +
+ (loc.row << adev->row_shift);
+}
+
+/**
+ * aie_validate_location() - validate tile location within an AI engine
+ * partition
+ * @apart: AI engine partition
+ * @loc: AI engine tile location
+ * @return: return 0 if it is valid, negative value for errors.
+ *
+ * This function checks if the AI engine location is within the AI engine
+ * partition.
+ */
+static inline int aie_validate_location(struct aie_partition *apart,
+ struct aie_location loc)
+{
+ if (loc.col < apart->range.start.col ||
+ loc.col >= apart->range.start.col + apart->range.size.col ||
+ loc.row < apart->range.start.row ||
+ loc.row >= apart->range.start.row + apart->range.size.row)
+ return -EINVAL;
+
+ return 0;
+}
+
+int aie_resource_initialize(struct aie_resource *res, int count);
+void aie_resource_uninitialize(struct aie_resource *res);
+int aie_resource_check_region(struct aie_resource *res, u32 start,
+ u32 count);
+int aie_resource_get_region(struct aie_resource *res, u32 start,
+ u32 count);
+void aie_resource_put_region(struct aie_resource *res, int start, u32 count);
+
+const struct file_operations *aie_part_get_fops(void);
+u8 aie_part_in_use(struct aie_partition *apart);
+struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
+ u32 partition_id);
+struct aie_partition *aie_request_partition(struct aie_device *adev,
+ struct aie_partition_req *req);
+struct aie_partition *of_aie_part_probe(struct aie_device *adev,
+ struct device_node *nc);
+void aie_part_remove(struct aie_partition *apart);
+
+int aie_device_init(struct aie_device *adev);
+#endif /* AIE_INTERNAL_H */
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
new file mode 100644
index 0000000..fc8f9f5
--- /dev/null
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
@@ -0,0 +1,498 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx AI Engine partition driver
+ *
+ * Copyright (C) 2020 Xilinx, Inc.
+ */
+
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/mmu_context.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+#include <uapi/linux/xlnx-ai-engine.h>
+
+#include "ai-engine-internal.h"
+
+/**
+ * aie_cal_loc() - calculate tile location from register offset to the AI
+ * engine device
+ * @adev: AI engine device
+ * @loc: memory pointer to restore returning location information
+ * @regoff: tile internal register offset
+ *
+ * This function returns the tile location.
+ */
+static void aie_cal_loc(struct aie_device *adev,
+ struct aie_location *loc, u64 regoff)
+{
+ loc->col = (u32)aie_tile_reg_field_get(aie_col_mask(adev),
+ adev->col_shift, regoff);
+ loc->row = (u32)aie_tile_reg_field_get(aie_row_mask(adev),
+ adev->row_shift, regoff);
+}
+
+/**
+ * aie_part_reg_validation() - validate AI engine partition register access
+ * @apart: AI engine partition
+ * @offset: AI engine register offset
+ * @len: len of data to write/read
+ * @is_write: is the access to write to register
+ * @return: 0 for success, or negative value for failure.
+ *
+ * This function validate if the register to access is within the AI engine
+ * partition. If it is write access, if the register is writable by user.
+ */
+static int aie_part_reg_validation(struct aie_partition *apart, size_t offset,
+ size_t len, u8 is_write)
+{
+ struct aie_device *adev;
+ u32 regend32, ttype;
+ u64 regoff, regend64;
+ struct aie_location loc;
+ unsigned int i;
+
+ adev = apart->adev;
+ if (offset % sizeof(u32)) {
+ dev_err(&apart->dev,
+ "Invalid reg off(0x%zx), not 32bit aligned.\n",
+ offset);
+ return -EINVAL;
+ }
+
+ if (len % sizeof(u32)) {
+ dev_err(&apart->dev, "Invalid reg operation len %zu.\n", len);
+ return -EINVAL;
+ }
+
+ regoff = aie_cal_tile_reg(adev, offset);
+ regend64 = regoff + len;
+ if (regend64 >= BIT_ULL(adev->row_shift)) {
+ dev_err(&apart->dev,
+ "Invalid reg operation len %zu.\n", len);
+ return -EINVAL;
+ }
+
+ aie_cal_loc(adev, &loc, offset);
+ if (aie_validate_location(apart, loc)) {
+ dev_err(&apart->dev,
+ "Invalid (%d,%d) out of part(%d,%d),(%d,%d)\n",
+ loc.col, loc.row,
+ apart->range.start.col, apart->range.start.row,
+ apart->range.size.col, apart->range.size.row);
+ return -EINVAL;
+ }
+
+ if (!is_write)
+ return 0;
+
+ regend32 = lower_32_bits(regend64);
+ ttype = adev->ops->get_tile_type(&loc);
+ for (i = 0; i < adev->num_kernel_regs; i++) {
+ const struct aie_tile_regs *regs;
+ u32 rttype, writable;
+
+ regs = &adev->kernel_regs[i];
+ rttype = (regs->attribute & AIE_REGS_ATTR_TILE_TYPE_MASK) >>
+ AIE_REGS_ATTR_TILE_TYPE_SHIFT;
+ writable = (regs->attribute & AIE_REGS_ATTR_PERM_MASK) >>
+ AIE_REGS_ATTR_PERM_SHIFT;
+ if (!(ttype & rttype))
+ continue;
+ if ((regoff >= regs->soff && regoff <= regs->eoff) ||
+ (regend32 >= regs->soff && regend32 <= regs->eoff)) {
+ if (!writable) {
+ dev_err(&apart->dev,
+ "reg 0x%zx,0x%zx not writable.\n",
+ offset, len);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * aie_part_write_register() - AI engine partition write register
+ * @apart: AI engine partition
+ * @offset: AI engine register offset
+ * @len: len of data to write
+ * @data: data to write
+ * @mask: mask, if it is non 0, it is mask write.
+ * @return: number of bytes write for success, or negative value for failure.
+ *
+ * This function writes data to the specified registers.
+ * If the mask is non 0, it is mask write.
+ */
+static int aie_part_write_register(struct aie_partition *apart, size_t offset,
+ size_t len, void *data, u32 mask)
+{
+ int ret;
+ void __iomem *va;
+
+ if (mask && len > sizeof(u32)) {
+ /* For mask write, only allow 32bit. */
+ dev_err(&apart->dev,
+ "failed mask write, len is more that 32bit.\n");
+ return -EINVAL;
+ }
+
+ /* offset is expected to be relative to the start of the partition */
+ offset += aie_cal_regoff(apart->adev, apart->range.start, 0);
+ ret = aie_part_reg_validation(apart, offset, len, 1);
+ if (ret < 0) {
+ dev_err(&apart->dev, "failed to write to 0x%zx,0x%zx.\n",
+ offset, len);
+ return ret;
+ }
+
+ va = apart->adev->base + offset;
+ if (!mask) {
+ if (len == sizeof(u32))
+ iowrite32(*((u32 *)data), va);
+ else
+ memcpy_toio(va, data, len);
+ } else {
+ u32 val = ioread32(va);
+
+ val &= ~mask;
+ val |= *((u32 *)data) & mask;
+ iowrite32(val, va);
+ }
+
+ return (int)len;
+}
+
+/**
+ * aie_part_access_regs() - AI engine partition registers access
+ * @apart: AI engine partition
+ * @num_reqs: number of access requests
+ * @reqs: array of registers access
+ * @return: 0 for success, and negative value for failure.
+ *
+ * This function executes AI engine partition register access requests.
+ */
+static int aie_part_access_regs(struct aie_partition *apart, u32 num_reqs,
+ struct aie_reg_args *reqs)
+{
+ u32 i;
+
+ for (i = 0; i < num_reqs; i++) {
+ struct aie_reg_args *args = &reqs[i];
+ int ret;
+
+ if (args->op != AIE_REG_WRITE) {
+ dev_err(&apart->dev,
+ "Invalid register command type: %u.\n",
+ args->op);
+ return -EINVAL;
+ }
+ ret = aie_part_write_register(apart,
+ (size_t)args->offset,
+ sizeof(args->val),
+ &args->val, args->mask);
+ if (ret < 0) {
+ dev_err(&apart->dev, "reg op %u failed: 0x%llx.\n",
+ args->op, args->offset);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aie_part_release(struct inode *inode, struct file *filp)
+{
+ struct aie_partition *apart = filp->private_data;
+ int ret;
+
+ /*
+ * TODO: It will need to reset the SHIM columns and gate the
+ * tiles of the partition.
+ */
+ ret = mutex_lock_interruptible(&apart->mlock);
+ if (ret)
+ return ret;
+
+ apart->status = 0;
+ mutex_unlock(&apart->mlock);
+
+ return 0;
+}
+
+static const struct vm_operations_struct aie_part_physical_vm_ops = {
+#ifdef CONFIG_HAVE_IOREMAP_PROT
+ .access = generic_access_phys,
+#endif
+};
+
+static int aie_part_mmap(struct file *fp, struct vm_area_struct *vma)
+{
+ struct aie_partition *apart = fp->private_data;
+ struct aie_device *adev = apart->adev;
+ unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
+ phys_addr_t addr;
+ size_t size;
+
+ if (vma->vm_end < vma->vm_start)
+ return -EINVAL;
+ /* Only allow userspace directly read registers */
+ if (vma->vm_flags & VM_WRITE) {
+ dev_err(&apart->dev, "%s: do not support writable mmap.\n",
+ __func__);
+ return -EINVAL;
+ }
+ vma->vm_private_data = apart;
+ vma->vm_ops = &aie_part_physical_vm_ops;
+ size = apart->range.size.col << adev->col_shift;
+ if ((vma->vm_end - vma->vm_start) > (size - offset)) {
+ dev_err(&apart->dev,
+ "%s: size exceed.\n", __func__);
+ return -EINVAL;
+ }
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ /* Calculate the partition address */
+ addr = adev->res->start;
+ addr += apart->range.start.col << adev->col_shift;
+ addr += apart->range.start.row << adev->row_shift;
+ addr += offset;
+ return remap_pfn_range(vma,
+ vma->vm_start,
+ addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+}
+
+static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ struct aie_partition *apart = fp->private_data;
+ void __user *argp = (void __user *)arg;
+ long ret;
+
+ switch (cmd) {
+ case AIE_REG_IOCTL:
+ {
+ struct aie_reg_args raccess;
+
+ if (copy_from_user(&raccess, argp, sizeof(raccess)))
+ return -EFAULT;
+
+ ret = mutex_lock_interruptible(&apart->mlock);
+ if (ret)
+ return ret;
+
+ ret = aie_part_access_regs(apart, 1, &raccess);
+ mutex_unlock(&apart->mlock);
+ break;
+ }
+ default:
+ dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+const struct file_operations aie_part_fops = {
+ .owner = THIS_MODULE,
+ .release = aie_part_release,
+ .mmap = aie_part_mmap,
+ .unlocked_ioctl = aie_part_ioctl,
+};
+
+/**
+ * aie_part_release_device() - release an AI engine partition instance
+ * @dev: AI engine partition device
+ *
+ * It will be called by device driver core when no one holds a valid
+ * pointer to @dev anymore.
+ */
+static void aie_part_release_device(struct device *dev)
+{
+ struct aie_partition *apart = dev_to_aiepart(dev);
+ struct aie_device *adev = apart->adev;
+ int ret;
+
+ ret = mutex_lock_interruptible(&adev->mlock);
+ if (ret) {
+ dev_warn(&apart->dev,
+ "getting adev->mlock is interrupted by signal\n");
+ }
+
+ aie_resource_put_region(&adev->cols_res, apart->range.start.col,
+ apart->range.size.col);
+ list_del(&apart->node);
+ mutex_unlock(&adev->mlock);
+ put_device(apart->dev.parent);
+}
+
+/**
+ * aie_create_partition() - create AI engine partition instance
+ * @adev: AI engine device
+ * @range: AI engine partition range to check. A range describes a group
+ * of AI engine tiles.
+ * @return: created AI engine partition pointer for success, and PTR_ERR
+ * for failure.
+ *
+ * This function creates an AI engine partition instance.
+ * It creates AI engine partition, the AI engine partition device and
+ * the AI engine partition character device.
+ */
+static struct aie_partition *aie_create_partition(struct aie_device *adev,
+ struct aie_range *range)
+{
+ struct aie_partition *apart;
+ struct device *dev;
+ char devname[32];
+ int ret;
+
+ ret = mutex_lock_interruptible(&adev->mlock);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = aie_resource_check_region(&adev->cols_res, range->start.col,
+ range->size.col);
+ if (ret != range->start.col) {
+ dev_err(&adev->dev, "invalid partition (%u,%u)(%u,%u).\n",
+ range->start.col, range->start.row,
+ range->size.col, range->size.row);
+ mutex_unlock(&adev->mlock);
+ return ERR_PTR(-EINVAL);
+ }
+ ret = aie_resource_get_region(&adev->cols_res, range->start.col,
+ range->size.col);
+ if (ret != range->start.col) {
+ dev_err(&adev->dev, "failed to get partition (%u,%u)(%u,%u).\n",
+ range->start.col, range->start.row,
+ range->size.col, range->size.row);
+ mutex_unlock(&adev->mlock);
+ return ERR_PTR(-EFAULT);
+ }
+ mutex_unlock(&adev->mlock);
+
+ apart = devm_kzalloc(&adev->dev, sizeof(*apart), GFP_KERNEL);
+ if (!apart)
+ return ERR_PTR(-ENOMEM);
+
+ apart->adev = adev;
+ memcpy(&apart->range, range, sizeof(*range));
+ mutex_init(&apart->mlock);
+
+ /* Create AI engine partition device */
+ dev = &apart->dev;
+ device_initialize(dev);
+ dev->parent = &adev->dev;
+ dev->class = aie_class;
+ dev_set_drvdata(dev, apart);
+ snprintf(devname, sizeof(devname) - 1, "aiepart_%d_%d",
+ apart->range.start.col, apart->range.size.col);
+ dev_set_name(dev, devname);
+ /* We can now rely on the release function for cleanup */
+ dev->release = aie_part_release_device;
+ ret = device_add(dev);
+ if (ret) {
+ dev_err(dev, "device_add failed: %d\n", ret);
+ put_device(dev);
+ return ERR_PTR(ret);
+ }
+
+ ret = mutex_lock_interruptible(&adev->mlock);
+ if (ret) {
+ put_device(dev);
+ return ERR_PTR(ret);
+ }
+
+ list_add_tail(&apart->node, &adev->partitions);
+ mutex_unlock(&adev->mlock);
+ get_device(&adev->dev);
+ dev_dbg(dev, "created AIE partition device.\n");
+
+ return apart;
+}
+
+struct aie_partition *
+of_aie_part_probe(struct aie_device *adev, struct device_node *nc)
+{
+ struct aie_partition *apart;
+ struct aie_range range;
+ u32 partition_id, regs[4];
+ int ret;
+
+ /* Select device driver */
+ ret = of_property_read_u32_array(nc, "reg", regs, ARRAY_SIZE(regs));
+ if (ret < 0) {
+ dev_err(&adev->dev,
+ "probe %pOF failed, no tiles range information.\n",
+ nc);
+ return ERR_PTR(ret);
+ }
+ range.start.col = regs[0];
+ range.start.row = regs[1];
+ range.size.col = regs[2];
+ range.size.row = regs[3];
+
+ ret = of_property_read_u32_index(nc, "xlnx,partition-id", 0,
+ &partition_id);
+ if (ret < 0) {
+ dev_err(&adev->dev,
+ "probe %pOF failed, no partition id.\n", nc);
+ return ERR_PTR(ret);
+ }
+
+ ret = mutex_lock_interruptible(&adev->mlock);
+ if (ret)
+ return ERR_PTR(ret);
+
+ apart = aie_get_partition_from_id(adev, partition_id);
+ mutex_unlock(&adev->mlock);
+ if (apart) {
+ dev_err(&adev->dev,
+ "probe failed: partition %u exists.\n",
+ partition_id);
+ return ERR_PTR(ret);
+ }
+
+ apart = aie_create_partition(adev, &range);
+ if (IS_ERR(apart)) {
+ dev_err(&adev->dev,
+ "%s: failed to create part(%u,%u),(%u,%u).\n",
+ __func__, range.start.col, range.start.row,
+ range.size.col, range.size.row);
+ return apart;
+ }
+
+ of_node_get(nc);
+ apart->dev.of_node = nc;
+ apart->partition_id = partition_id;
+
+ dev_info(&adev->dev,
+ "AI engine part(%u,%u),(%u,%u), id %u is probed successfully.\n",
+ range.start.col, range.start.row,
+ range.size.col, range.size.row, apart->partition_id);
+
+ return apart;
+}
+
+/**
+ * aie_destroy_part() - destroy AI engine partition
+ * @apart: AI engine partition
+ *
+ * This function will remove AI engine partition.
+ */
+void aie_part_remove(struct aie_partition *apart)
+{
+ device_del(&apart->dev);
+ put_device(&apart->dev);
+}
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-res.c b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
new file mode 100644
index 0000000..36f08bf
--- /dev/null
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx AI Engine device driver
+ *
+ * Copyright (C) 2020 Xilinx, Inc.
+ */
+
+#include <linux/bitmap.h>
+
+#include "ai-engine-internal.h"
+
+/**
+ * aie_resource_initialize() - initialize AI engine resource
+ * @res: pointer to AI engine resource
+ * @count: total number of element of this resource
+ * @return: 0 for success, negative value for failure.
+ *
+ * This function will initialize the data structure for the
+ * resource.
+ */
+int aie_resource_initialize(struct aie_resource *res, int count)
+{
+ if (!res || !count)
+ return -EINVAL;
+ res->bitmap = bitmap_zalloc(count, GFP_KERNEL);
+ if (!res->bitmap)
+ return -ENOMEM;
+ res->total = count;
+
+ return 0;
+}
+
+/**
+ * aie_resource_uninitialize() - uninitialize AI engine resource
+ * @res: pointer to AI engine resource
+ *
+ * This function will release the AI engine resource data members.
+ */
+void aie_resource_uninitialize(struct aie_resource *res)
+{
+ res->total = 0;
+ if (res->bitmap)
+ bitmap_free(res->bitmap);
+}
+
+/**
+ * aie_resource_check() - check availability of requested resource
+ * @res: pointer to AI engine resource to check
+ * @start: start index of the required resource, it will only be used if
+ * @continuous is 1. It will check the available resource starting from
+ * @start
+ * @count: number of requested element
+ * @return: start resource id if the requested number of resources are available
+ * It will return negative value of errors.
+ *
+ * This function will check the availability. It will return start resource id
+ * if the requested number of resources are available.
+ */
+int aie_resource_check_region(struct aie_resource *res,
+ u32 start, u32 count)
+{
+ unsigned long id;
+
+ if (!res || !res->bitmap || !count)
+ return -EINVAL;
+ id = bitmap_find_next_zero_area(res->bitmap, res->total, start,
+ count, 0);
+ if (id >= res->total)
+ return -ERANGE;
+
+ return (int)id;
+}
+
+/**
+ * aie_resource_get_region() - get requested AI engine resource
+ * @res: pointer to AI engine resource to check
+ * @count: number of requested element
+ * @start: start index of the required resource
+ * @return: start resource id for success, and negative value for failure.
+ *
+ * This function check if the requested AI engine resource is available.
+ * If it is available, mark it used and return the start resource id.
+ */
+int aie_resource_get_region(struct aie_resource *res, u32 start, u32 count)
+{
+ unsigned long off;
+
+ if (!res || !res->bitmap || !count)
+ return -EINVAL;
+ off = bitmap_find_next_zero_area(res->bitmap, res->total, start,
+ count, 0);
+ if (off >= res->total) {
+ pr_err("Failed to get available AI engine resource.\n");
+ return -ERANGE;
+ }
+ bitmap_set(res->bitmap, off, count);
+
+ return (int)off;
+}
+
+/**
+ * aie_resource_put_region() - release requested AI engine resource
+ * @res: pointer to AI engine resource to check
+ * @start: start index of the resource to release
+ * @count: number of elements to release
+ *
+ * This function release the requested AI engine resource.
+ */
+void aie_resource_put_region(struct aie_resource *res, int start, u32 count)
+{
+ if (!res || !count)
+ return;
+ bitmap_clear(res->bitmap, start, count);
+}
diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-engine.h
new file mode 100644
index 0000000..acbc781
--- /dev/null
+++ b/include/uapi/linux/xlnx-ai-engine.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2020, Xilinx Inc.
+ */
+
+#ifndef _UAPI_AI_ENGINE_H_
+#define _UAPI_AI_ENGINE_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+enum aie_reg_op {
+ AIE_REG_WRITE,
+};
+
+/* AI engine partition is in use */
+#define XAIE_PART_STATUS_INUSE (1U << 0)
+
+/**
+ * struct aie_location - AIE location information
+ * @col: column id
+ * @row: row id
+ */
+struct aie_location {
+ __u32 col;
+ __u32 row;
+};
+
+/**
+ * struct aie_range - AIE range information
+ * @start: start tile location
+ * @size: size of the range, number of columns and rows
+ */
+struct aie_range {
+ struct aie_location start;
+ struct aie_location size;
+};
+
+/**
+ * struct aie_reg_args - AIE access register arguments
+ * @op: if this request is to read, write or poll register
+ * @mask: mask for mask write, 0 for not mask write
+ * @offset: offset of register to the start of an AI engine partition
+ * @val: value to write or get
+ */
+struct aie_reg_args {
+ enum aie_reg_op op;
+ __u32 mask;
+ __u64 offset;
+ __u32 val;
+};
+
+/**
+ * struct aie_range_args - AIE range request arguments
+ * @partition_id: partition id. It is used to identify the
+ * AI engine partition in the system.
+ * @uid: image identifier loaded on the AI engine partition
+ * @range: range of AIE tiles
+ * @status: indicate if the AI engine is in use.
+ * 0 means not in used, otherwise, in use.
+ */
+struct aie_range_args {
+ __u32 partition_id;
+ __u32 uid;
+ struct aie_range range;
+ __u32 status;
+};
+
+/**
+ * struct aie_partition_query - AIE partition query arguments
+ * @partition_cnt: number of defined partitions in the system
+ * @partitions: buffer to store defined partitions information.
+ */
+struct aie_partition_query {
+ struct aie_range_args *partitions;
+ __u32 partition_cnt;
+};
+
+/**
+ * struct aie_partition_req - AIE request partition arguments
+ * @partition_id: partition node id. It is used to identify the AI engine
+ * partition in the system.
+ * @uid: image identifier loaded on the AI engine partition
+ * @meta_data: meta data to indicate which resources used by application.
+ * @flag: used for application to indicate particular driver requirements
+ * application wants to have for the partition. e.g. do not clean
+ * resource when closing the partition.
+ */
+struct aie_partition_req {
+ __u32 partition_id;
+ __u32 uid;
+ __u64 meta_data;
+ __u32 flag;
+};
+
+#define AIE_IOCTL_BASE 'A'
+
+/* AI engine device IOCTL operations */
+#define AIE_ENQUIRE_PART_IOCTL _IOWR(AIE_IOCTL_BASE, 0x1, \
+ struct aie_partition_query)
+#define AIE_REQUEST_PART_IOCTL _IOR(AIE_IOCTL_BASE, 0x2, \
+ struct aie_partition_req)
+
+/* AI engine partition IOCTL operations */
+#define AIE_REG_IOCTL _IOWR(AIE_IOCTL_BASE, 0x8, \
+ struct aie_reg_args)
+#endif
--
2.7.4

2020-11-18 08:11:45

by Wendy Liang

[permalink] [raw]
Subject: [PATCH 4/9] misc: xilinx-ai-engine: expose AI engine tile memories to userspace

There is no concern to have userspace to directly access AI engine
program and data memories. It will be much faster to directly copy
data to and from these memories from userspace.

We choose to use DMA buf for the data and program memory because of the
DMA buf features. DMA buf can share the DMA memory between applications
and different devices, which can benefit on how to share data with AI
engine device in future.

There is one DMA buf per type of memory in an AI engine partition. e.g.
There is one DMA buf for all the tile core program memories in an AI
engine partition. There is another DMA buf for all the tile data
memories in an AI engine partition.

Signed-off-by: Wendy Liang <[email protected]>
Reviewed-by: Hyun Kwon <[email protected]>
---
drivers/misc/xilinx-ai-engine/Makefile | 1 +
drivers/misc/xilinx-ai-engine/ai-engine-aie.c | 36 +++
drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 30 +++
drivers/misc/xilinx-ai-engine/ai-engine-mem.c | 274 +++++++++++++++++++++
drivers/misc/xilinx-ai-engine/ai-engine-part.c | 47 ++++
drivers/misc/xilinx-ai-engine/ai-engine-reset.c | 38 +++
include/uapi/linux/xlnx-ai-engine.h | 49 ++++
7 files changed, 475 insertions(+)
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-mem.c

diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-engine/Makefile
index 39bec61..2dbed42 100644
--- a/drivers/misc/xilinx-ai-engine/Makefile
+++ b/drivers/misc/xilinx-ai-engine/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_XILINX_AIE) += xilinx-aie.o

xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \
ai-engine-dev.o \
+ ai-engine-mem.o \
ai-engine-part.o \
ai-engine-res.o \
ai-engine-reset.o
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
index 36127f0..7fce2f00 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
@@ -12,10 +12,14 @@

#include "ai-engine-internal.h"

+#define KBYTES(n) ((n) * 1024)
+
#define AIE_ARRAY_SHIFT 30U
#define AIE_COL_SHIFT 23U
#define AIE_ROW_SHIFT 18U

+#define NUM_MEMS_PER_TILE 2U
+
/*
* Registers offsets
*/
@@ -114,6 +118,37 @@ static u32 aie_get_tile_type(struct aie_location *loc)
return AIE_TILE_TYPE_SHIMNOC;
}

+static unsigned int aie_get_mem_info(struct aie_range *range,
+ struct aie_part_mem *pmem)
+{
+ unsigned int i;
+
+ if (range->start.row + range->size.row <= 1) {
+ /* SHIM row only, no memories in this range */
+ return 0;
+ }
+ if (!pmem)
+ return NUM_MEMS_PER_TILE;
+
+ for (i = 0; i < NUM_MEMS_PER_TILE; i++) {
+ struct aie_mem *mem = &pmem[i].mem;
+
+ memcpy(&mem->range, range, sizeof(*range));
+ if (!mem->range.start.row) {
+ mem->range.start.row = 1;
+ mem->range.size.row--;
+ }
+ }
+ /* Setup tile data memory information */
+ pmem[0].mem.offset = 0;
+ pmem[0].mem.size = KBYTES(32);
+ /* Setup program memory information */
+ pmem[1].mem.offset = 0x20000;
+ pmem[1].mem.size = KBYTES(16);
+
+ return NUM_MEMS_PER_TILE;
+}
+
/**
* aie_set_shim_reset() - Set AI engine SHIM reset
* @adev: AI engine device
@@ -170,6 +205,7 @@ static int aie_reset_shim(struct aie_device *adev, struct aie_range *range)

static const struct aie_tile_operations aie_ops = {
.get_tile_type = aie_get_tile_type,
+ .get_mem_info = aie_get_mem_info,
.reset_shim = aie_reset_shim,
};

diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
index 2acd34f..e84610b 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
@@ -12,6 +12,8 @@
#include <linux/bits.h>
#include <linux/cdev.h>
#include <linux/device.h>
+#include <linux/dma-buf.h>
+#include <linux/file.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/mutex.h>
@@ -67,8 +69,30 @@ struct aie_device;
struct aie_partition;

/**
+ * struct aie_part_mem - AI engine partition memory information structure
+ * @apart: AI engine partition
+ * @dbuf: dmabuf pointer associated with the memory
+ * @mem: memory information of a type of memory
+ * @size: size of the total memories in the partition
+ *
+ * This structure is to keep the information of a type of memory in a
+ * partition. The memory information will be stored in @mem property.
+ * The following information will be keep:
+ * * memory start address offset within a tile
+ * * memory size
+ * * what tiles contain this type of memory
+ */
+struct aie_part_mem {
+ struct aie_partition *apart;
+ struct dma_buf *dbuf;
+ struct aie_mem mem;
+ size_t size;
+};
+
+/**
* struct aie_tile_operations - AI engine device operations
* @get_tile_type: get type of tile based on tile operation
+ * @get_mem_info: get different types of memories information
* @reset_shim: reset shim, it will assert and then release SHIM reset
*
* Different AI engine device version has its own device
@@ -76,6 +100,8 @@ struct aie_partition;
*/
struct aie_tile_operations {
u32 (*get_tile_type)(struct aie_location *loc);
+ unsigned int (*get_mem_info)(struct aie_range *range,
+ struct aie_part_mem *pmem);
int (*reset_shim)(struct aie_device *adev, struct aie_range *range);
};

@@ -134,6 +160,7 @@ struct aie_device {
* struct aie_partition - AI engine partition structure
* @node: list node
* @adev: pointer to AI device instance
+ * @pmems: pointer to partition memories types
* @range: range of partition
* @mlock: protection for AI engine partition operations
* @dev: device for the AI engine partition
@@ -146,6 +173,7 @@ struct aie_device {
struct aie_partition {
struct list_head node;
struct aie_device *adev;
+ struct aie_part_mem *pmems;
struct aie_range range;
struct mutex mlock; /* protection for AI engine partition operations */
struct device dev;
@@ -256,5 +284,7 @@ struct aie_partition *of_aie_part_probe(struct aie_device *adev,
void aie_part_remove(struct aie_partition *apart);
int aie_part_clean(struct aie_partition *apart);

+int aie_mem_get_info(struct aie_partition *apart, unsigned long arg);
+
int aie_device_init(struct aie_device *adev);
#endif /* AIE_INTERNAL_H */
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-mem.c b/drivers/misc/xilinx-ai-engine/ai-engine-mem.c
new file mode 100644
index 0000000..5a06bdd0
--- /dev/null
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-mem.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx AI Engine device memory implementation
+ *
+ * Copyright (C) 2020 Xilinx, Inc.
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/xlnx-ai-engine.h>
+
+#include "ai-engine-internal.h"
+
+#define aie_cal_reg_goffset(adev, loc, regoff) ({ \
+ struct aie_device *_adev = (adev); \
+ struct aie_location *_loc = &(loc); \
+ (_loc->col << _adev->col_shift) + \
+ (_loc->row << _adev->row_shift) + (regoff); \
+ })
+
+#define aie_cal_reg_pa(adev, loc, regoff) ({ \
+ struct aie_device *__adev = (adev); \
+ __adev->res->start + aie_cal_reg_goffset(__adev, loc, regoff); \
+ })
+
+static struct sg_table *
+aie_mem_map_dma_buf(struct dma_buf_attachment *attachment,
+ enum dma_data_direction direction)
+{
+ /*
+ * TODO: It is mandatory by DMA buf operation. It is used return
+ * scatterlist table of an attachment. We don't have the implementation
+ * for now. And thus it has empty implementation.
+ */
+ (void)attachment;
+ (void)direction;
+ dev_warn(attachment->dev,
+ "AI engine memory map dma buf is not implemented.\n");
+ return NULL;
+}
+
+static void aie_mem_unmap_dma_buf(struct dma_buf_attachment *attachment,
+ struct sg_table *table,
+ enum dma_data_direction direction)
+{
+ /*
+ * TODO: It is mandatory by DMA buf operation. It is used deallocate
+ * scatterlist table of an attachment. We don't have the implementation
+ * for now. And thus it has empty implementation.
+ */
+ (void)attachment;
+ (void)table;
+ (void)direction;
+ dev_warn(attachment->dev,
+ "AI engine memory unmap dma buf is not implemented.\n");
+}
+
+static int aie_mem_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+ struct aie_part_mem *pmem = dmabuf->priv;
+ struct aie_mem *mem = &pmem->mem;
+ struct aie_partition *apart = pmem->apart;
+ struct aie_location loc;
+ unsigned long addr = vma->vm_start;
+ unsigned long offset = vma->vm_pgoff * PAGE_SIZE, moffset = 0;
+ unsigned long remainder = vma->vm_end - addr;
+ size_t msize = mem->size;
+
+ if (remainder + offset > pmem->size)
+ return -EINVAL;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ for (loc.col = mem->range.start.col;
+ loc.col < mem->range.start.col + mem->range.size.col; loc.col++) {
+ for (loc.row = mem->range.start.row;
+ loc.row < mem->range.start.row + mem->range.size.row;
+ loc.row++) {
+ unsigned long toffset, len;
+ phys_addr_t mempa;
+ int ret;
+
+ remainder = vma->vm_end - addr;
+ if (!remainder)
+ return 0;
+
+ if (moffset + msize < offset) {
+ moffset += msize;
+ continue;
+ }
+ /*
+ * calculate offset within the tile memory.
+ * offset is the offset to vma->start.
+ * moffset is the tile memory start offset to
+ * vma->start.
+ */
+ toffset = offset - moffset;
+ len = msize - toffset;
+ if (len > remainder)
+ len = remainder;
+ mempa = aie_cal_reg_pa(apart->adev, loc,
+ toffset + mem->offset);
+
+ ret = remap_pfn_range(vma, addr, mempa >> PAGE_SHIFT,
+ len, vma->vm_page_prot);
+ if (ret) {
+ dev_err(&apart->dev,
+ "failed to mmap (%u,%u)memory, remap failed, 0x%pa, 0x%lx.\n",
+ loc.col, loc.row, &mempa, len);
+ return ret;
+ }
+ addr += len;
+ offset += len;
+ moffset += msize;
+ }
+ }
+ return 0;
+}
+
+static void aie_mem_dmabuf_release(struct dma_buf *dmabuf)
+{
+ struct aie_part_mem *pmem = dmabuf->priv;
+
+ pmem->dbuf = NULL;
+}
+
+static const struct dma_buf_ops aie_mem_dma_buf_ops = {
+ .map_dma_buf = aie_mem_map_dma_buf,
+ .unmap_dma_buf = aie_mem_unmap_dma_buf,
+ .mmap = aie_mem_mmap,
+ .release = aie_mem_dmabuf_release,
+};
+
+/**
+ * aie_mem_create_dmabuf() - creates DMA buffer for AI engine partition
+ * memories
+ * @apart: AI engine partition
+ * @pmem: pointer to the partition memory information
+ * @mem: pointer to where it store the memory information and DMA buf file
+ * descriptor for user.
+ * @return: 0 for success, negative value for failure
+ *
+ * This function will create DMA buffer for the AI engine partition memory
+ * and will store the DMA buffer file descriptor and memory information in
+ * @mem.
+ */
+static int aie_mem_create_dmabuf(struct aie_partition *apart,
+ struct aie_part_mem *pmem,
+ struct aie_mem *mem)
+{
+ struct dma_buf *dmabuf;
+ int ret;
+
+ if (!PAGE_ALIGNED(pmem->mem.size)) {
+ dev_warn(&apart->dev,
+ "no dmabuf for mem(0x%zx, 0x%zx), not aligned with page size.\n",
+ pmem->mem.offset, pmem->mem.size);
+ return -EINVAL;
+ }
+
+ dmabuf = pmem->dbuf;
+ if (!dmabuf) {
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+ exp_info.ops = &aie_mem_dma_buf_ops;
+ exp_info.size = pmem->size;
+ exp_info.flags = O_RDWR;
+ exp_info.priv = pmem;
+
+ dmabuf = dma_buf_export(&exp_info);
+ if (IS_ERR(dmabuf))
+ return PTR_ERR(dmabuf);
+
+ pmem->dbuf = dmabuf;
+ }
+
+ ret = dma_buf_fd(dmabuf, O_CLOEXEC);
+ if (ret < 0) {
+ dev_err(&apart->dev,
+ "dmabuf creation failed, failed to get fd.\n");
+ return ret;
+ }
+ memcpy(mem, &pmem->mem, sizeof(*mem));
+ mem->fd = ret;
+
+ return 0;
+}
+
+/**
+ * aie_mem_get_info() - get AI engine memories information
+ * @apart: AI engine partition
+ * @arg: argument from user to enquire AI engine partition memory information
+ * @return: 0 for success, and negative value for failure
+ *
+ * This function will get the memories information for the specified AI engine
+ * partition. It will create DMA buf file descriptors for the memories and
+ * return the DMA buf file descriptors to users.
+ * It will create a DMA buffer per type of memories.
+ * e.g. There will be a DMA buffer for all the tile program memories in the
+ * partition, and another DMA buffer for all the tile data memories in the
+ * partition.
+ * User can first pass num_mems as 0 in the @arg to enquire for how many types
+ * of memories in this AI engine partition. And then, user can allocate memory
+ * to keep the information for different types of memories, and then use the
+ * same enquiry with non-zero num_mems and none NULL pointer to ask for the
+ * details of the information of all the types of memories in the AI engine
+ * partition.
+ */
+int aie_mem_get_info(struct aie_partition *apart, unsigned long arg)
+{
+ struct aie_mem_args margs;
+ struct aie_mem *mems;
+ unsigned int num_mems, i;
+ int ret;
+
+ if (copy_from_user(&margs, (void __user *)arg, sizeof(margs)))
+ return -EFAULT;
+
+ num_mems = apart->adev->ops->get_mem_info(&apart->range, NULL);
+ if (num_mems <= 0)
+ return -EINVAL;
+
+ if (!margs.num_mems) {
+ struct aie_mem_args __user *umargs_ptr = (void __user *)arg;
+
+ /* This enquiry is to get the number of types of memories. */
+ if (copy_to_user((void __user *)&umargs_ptr->num_mems,
+ &num_mems, sizeof(num_mems)))
+ return -EFAULT;
+ return 0;
+ }
+
+ if (num_mems != margs.num_mems) {
+ dev_err(&apart->dev,
+ "failed to get mem info, invalid num of mems %d,%d.\n",
+ num_mems, margs.num_mems);
+ return -EINVAL;
+ }
+ if (!margs.mems) {
+ dev_err(&apart->dev,
+ "failed to get mem info, mems pointer is NULL.\n");
+ return -EINVAL;
+ }
+
+ mems = kcalloc(num_mems, sizeof(*mems), GFP_KERNEL);
+ if (!mems)
+ return -ENOMEM;
+
+ /*
+ * Create DMA buffer for the memories.
+ * Each type of memory in the partition has its own DMA buf.
+ */
+ for (i = 0; i < num_mems; i++) {
+ ret = aie_mem_create_dmabuf(apart, &apart->pmems[i], &mems[i]);
+ if (ret)
+ break;
+ }
+ if (!ret) {
+ if (copy_to_user((void __user *)margs.mems, mems,
+ num_mems * sizeof(mems[0])))
+ ret = -EFAULT;
+ }
+
+ if (ret) {
+ for (i = 0; i < num_mems; i++) {
+ if (mems[i].fd)
+ put_unused_fd(mems[i].fd);
+ }
+ }
+
+ kfree(mems);
+ return ret;
+}
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
index 98f125b..4be6d38 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-part.c
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
@@ -294,6 +294,8 @@ static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
mutex_unlock(&apart->mlock);
break;
}
+ case AIE_GET_MEM_IOCTL:
+ return aie_mem_get_info(apart, arg);
default:
dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd);
ret = -EINVAL;
@@ -337,6 +339,41 @@ static void aie_part_release_device(struct device *dev)
}

/**
+ * aie_part_create_mems_info() - creates array to store the AI engine partition
+ * different memories types information
+ * @apart: AI engine partition
+ * @return: 0 for success, negative value for failure
+ *
+ * This function will create array to store the information of different
+ * memories types in the partition. This array is stored in @apart->pmems.
+ */
+static int aie_part_create_mems_info(struct aie_partition *apart)
+{
+ unsigned int i, num_mems;
+
+ num_mems = apart->adev->ops->get_mem_info(&apart->range, NULL);
+ if (!num_mems)
+ return 0;
+
+ apart->pmems = devm_kcalloc(&apart->dev, num_mems,
+ sizeof(struct aie_part_mem),
+ GFP_KERNEL);
+ if (!apart->pmems)
+ return -ENOMEM;
+
+ apart->adev->ops->get_mem_info(&apart->range, apart->pmems);
+ for (i = 0; i < num_mems; i++) {
+ struct aie_mem *mem = &apart->pmems[i].mem;
+
+ apart->pmems[i].apart = apart;
+ apart->pmems[i].size = mem->size *
+ mem->range.size.col *
+ mem->range.size.row;
+ }
+ return 0;
+}
+
+/**
* aie_create_partition() - create AI engine partition instance
* @adev: AI engine device
* @range: AI engine partition range to check. A range describes a group
@@ -406,6 +443,16 @@ static struct aie_partition *aie_create_partition(struct aie_device *adev,
return ERR_PTR(ret);
}

+ /*
+ * Create array to keep the information of the different types of tile
+ * memories information of the AI engine partition.
+ */
+ ret = aie_part_create_mems_info(apart);
+ if (ret) {
+ put_device(dev);
+ return ERR_PTR(ret);
+ }
+
ret = mutex_lock_interruptible(&adev->mlock);
if (ret) {
put_device(dev);
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-reset.c b/drivers/misc/xilinx-ai-engine/ai-engine-reset.c
index fc0262f7..d35cd8d 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-reset.c
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-reset.c
@@ -86,6 +86,43 @@ static void aie_part_set_cols_clkbuf(struct aie_partition *apart, bool enable)
}

/**
+ * aie_part_clear_mems() - clear memories of every tile in a partition
+ * @apart: AI engine partition
+ */
+static void aie_part_clear_mems(struct aie_partition *apart)
+{
+ struct aie_device *adev = apart->adev;
+ struct aie_part_mem *pmems = apart->pmems;
+ u32 i, num_mems;
+
+ /* Get the number of different types of memories */
+ num_mems = adev->ops->get_mem_info(&apart->range, NULL);
+ if (!num_mems)
+ return;
+
+ /* Clear each type of memories in the partition */
+ for (i = 0; i < num_mems; i++) {
+ struct aie_mem *mem = &pmems[i].mem;
+ struct aie_range *range = &mem->range;
+ u32 c, r;
+
+ for (c = range->start.col;
+ c < range->start.col + range->size.col; c++) {
+ for (r = range->start.row;
+ r < range->start.row + range->size.row; r++) {
+ struct aie_location loc;
+ u32 memoff;
+
+ loc.col = c;
+ loc.row = r;
+ memoff = aie_cal_regoff(adev, loc, mem->offset);
+ memset_io(adev->base + memoff, 0, mem->size);
+ }
+ }
+ }
+}
+
+/**
* aie_part_clean() - reset and clear AI engine partition
* @apart: AI engine partition
* @return: 0 for success and negative value for failure
@@ -115,6 +152,7 @@ int aie_part_clean(struct aie_partition *apart)
if (ret < 0)
return ret;

+ aie_part_clear_mems(apart);
aie_part_set_cols_clkbuf(apart, false);

return 0;
diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-engine.h
index ed2823c..5e40d00 100644
--- a/include/uapi/linux/xlnx-ai-engine.h
+++ b/include/uapi/linux/xlnx-ai-engine.h
@@ -6,6 +6,10 @@
#ifndef _UAPI_AI_ENGINE_H_
#define _UAPI_AI_ENGINE_H_

+#ifndef __KERNEL__
+#include <stdlib.h>
+#endif
+
#include <linux/ioctl.h>
#include <linux/types.h>

@@ -43,6 +47,32 @@ struct aie_range {
};

/**
+ * struct aie_mem - AIE memory information
+ * @range: range of tiles of the memory
+ * @offset: register offset within a tile of the memory
+ * @size: of a the memory in one tile
+ * @fd: file descriptor of the memory
+ */
+struct aie_mem {
+ struct aie_range range;
+ size_t offset;
+ size_t size;
+ int fd;
+};
+
+/**
+ * struct aie_mem_args - AIE memory enquiry arguments
+ * @num_mems: number of "struct aie_mem" elements
+ * e.g. two memory information elements, one for tile core memory,
+ * and the other for tile data memory.
+ * @mems: array of AI engine memory information elements
+ */
+struct aie_mem_args {
+ unsigned int num_mems;
+ struct aie_mem *mems;
+};
+
+/**
* struct aie_reg_args - AIE access register arguments
* @op: if this request is to read, write or poll register
* @mask: mask for mask write, 0 for not mask write
@@ -110,4 +140,23 @@ struct aie_partition_req {
/* AI engine partition IOCTL operations */
#define AIE_REG_IOCTL _IOWR(AIE_IOCTL_BASE, 0x8, \
struct aie_reg_args)
+/**
+ * DOC: AIE_GET_MEM_IOCTL - enquire information of memories in the AI engine
+ * partition
+ * This ioctl is used to get the information of all the different types of
+ * memories in the AI engine partition. Application can get the memories
+ * information in two steps:
+ * 1. passing 0 as @num_mems in struct aie_mem_args to enquire the number of
+ * different memories in the partition, the value will be returned in
+ * @num_mems.
+ * 2. passing the number of memories in @num_mems and valid pointer as @mems of
+ * struct aie_mem_args to store the details information of different
+ * memories. The driver will create DMA buf for each type of memories, and
+ * will return the memory addressing information along with the DMA buf file
+ * descriptors in @mems.
+ * After getting the memories information, user can use mmap() with the DMA buf
+ * file descriptor to enable access the memories from userspace.
+ */
+#define AIE_GET_MEM_IOCTL _IOWR(AIE_IOCTL_BASE, 0x9, \
+ struct aie_mem_args)
#endif
--
2.7.4

2020-11-18 08:12:16

by Wendy Liang

[permalink] [raw]
Subject: [PATCH 9/9] misc: xilinx-ai-engine: Add support for servicing error interrupts

From: Nishad Saraf <[email protected]>

AI engine errors events can be routed to generate interrupt. The
errors events routing will be done during AI engine configuration.
At runtime, Linux kernel AI engine driver monitors the interrupt and
backtracks errors events.
As error events from 400 AIE tiles and 50 shim tiles are channeled on
a single interrupt line, backtracking the source the interrupt to an
AIE module is required. To keep the top-half interrupt short,
backtracking is deferred to bottom half by scheduling a task in shared
workqueue.

Signed-off-by: Nishad Saraf <[email protected]>
Signed-off-by: Wendy Liang <[email protected]>
---
drivers/misc/xilinx-ai-engine/Makefile | 1 +
drivers/misc/xilinx-ai-engine/ai-engine-aie.c | 121 ++++
drivers/misc/xilinx-ai-engine/ai-engine-dev.c | 14 +
drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 144 +++++
.../misc/xilinx-ai-engine/ai-engine-interrupt.c | 661 +++++++++++++++++++++
drivers/misc/xilinx-ai-engine/ai-engine-part.c | 44 ++
drivers/misc/xilinx-ai-engine/ai-engine-res.c | 54 ++
7 files changed, 1039 insertions(+)
create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-interrupt.c

diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-engine/Makefile
index 2e67b25..9607ecb 100644
--- a/drivers/misc/xilinx-ai-engine/Makefile
+++ b/drivers/misc/xilinx-ai-engine/Makefile
@@ -9,6 +9,7 @@ xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \
ai-engine-clock.o \
ai-engine-dev.o \
ai-engine-dma.o \
+ ai-engine-interrupt.o \
ai-engine-mem.o \
ai-engine-part.o \
ai-engine-res.o \
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
index ff721b3..af0f997 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
@@ -33,7 +33,10 @@
#define AIE_SHIMPL_CLKCNTR_REGOFF 0x00036040U
#define AIE_SHIMPL_COLRESET_REGOFF 0x00036048U
#define AIE_SHIMPL_RESET_REGOFF 0x0003604cU
+#define AIE_SHIMPL_GROUP_ERROR_REGOFF 0x0003450cU
#define AIE_TILE_CORE_CLKCNTR_REGOFF 0x00036040U
+#define AIE_TILE_CORE_GROUP_ERROR_REGOFF 0x00034510U
+#define AIE_TILE_MEM_GROUP_ERROR_REGOFF 0x00014514U

/*
* Register masks
@@ -93,11 +96,27 @@ static const struct aie_tile_regs aie_kernel_regs[] = {
.soff = AIE_SHIMPL_CLKCNTR_REGOFF,
.eoff = AIE_SHIMPL_CLKCNTR_REGOFF,
},
+ /* SHIM group error enable */
+ {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
+ AIE_REGS_ATTR_TILE_TYPE_SHIFT,
+ .soff = AIE_SHIMPL_GROUP_ERROR_REGOFF,
+ .eoff = AIE_SHIMPL_GROUP_ERROR_REGOFF,
+ },
/* Tile clock control */
{.attribute = AIE_TILE_TYPE_TILE << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
.soff = AIE_TILE_CORE_CLKCNTR_REGOFF,
.eoff = AIE_TILE_CORE_CLKCNTR_REGOFF,
},
+ /* Tile group error for core module */
+ {.attribute = AIE_TILE_TYPE_TILE << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
+ .soff = AIE_TILE_CORE_GROUP_ERROR_REGOFF,
+ .eoff = AIE_TILE_CORE_GROUP_ERROR_REGOFF,
+ },
+ /* Tile group error for memory module */
+ {.attribute = AIE_TILE_TYPE_TILE << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
+ .soff = AIE_TILE_MEM_GROUP_ERROR_REGOFF,
+ .eoff = AIE_TILE_MEM_GROUP_ERROR_REGOFF,
+ },
};

static const struct aie_single_reg_field aie_col_rst = {
@@ -128,6 +147,103 @@ static const struct aie_dma_attr aie_shimdma = {
.bd_len = 0x14U,
};

+static const struct aie_event_attr aie_pl_event = {
+ .bc_event = {
+ .mask = GENMASK(6, 0),
+ .regoff = 0x0U,
+ },
+ .group_error = {
+ .mask = GENMASK(10, 0),
+ .regoff = 0xcU,
+ },
+ .bc_regoff = 0x34010U,
+ .status_regoff = 0x34200U,
+ .group_regoff = 0x34500U,
+ .base_error_event = 62U,
+ .num_broadcasts = 16U,
+ .base_bc_event = 107U,
+ .num_events = 128U,
+};
+
+static const struct aie_event_attr aie_mem_event = {
+ .bc_event = {
+ .mask = GENMASK(6, 0),
+ .regoff = 0x0U,
+ },
+ .group_error = {
+ .mask = GENMASK(13, 0),
+ .regoff = 0x14U,
+ },
+ .bc_regoff = 0x14010U,
+ .status_regoff = 0x14200U,
+ .group_regoff = 0x14500U,
+ .base_error_event = 87U,
+ .num_broadcasts = 16U,
+ .base_bc_event = 107U,
+ .num_events = 128U,
+};
+
+static const struct aie_event_attr aie_core_event = {
+ .bc_event = {
+ .mask = GENMASK(6, 0),
+ .regoff = 0x0U,
+ },
+ .group_error = {
+ .mask = GENMASK(21, 0),
+ .regoff = 0x10U,
+ },
+ .bc_regoff = 0x34010U,
+ .status_regoff = 0x34200U,
+ .group_regoff = 0x34500U,
+ .base_error_event = 48U,
+ .num_broadcasts = 16U,
+ .base_bc_event = 107U,
+ .num_events = 128U,
+};
+
+static const struct aie_l1_intr_ctrl_attr aie_l1_intr_ctrl = {
+ .swa_status = {
+ .mask = GENMASK(19, 0),
+ .regoff = 0xcU,
+ },
+ .swb_status = {
+ .mask = GENMASK(19, 0),
+ .regoff = 0x3cU,
+ },
+ .swa_event = {
+ .mask = GENMASK(6, 0),
+ .regoff = 0x14U,
+ },
+ .swb_event = {
+ .mask = GENMASK(6, 0),
+ .regoff = 0x44U,
+ },
+ .regoff = 0x35000U,
+ .event_lsb = 8,
+ .num_broadcasts = 0x14U,
+};
+
+static const struct aie_l2_intr_ctrl_attr aie_l2_intr_ctrl = {
+ .mask = {
+ .mask = GENMASK(15, 0),
+ .regoff = 0x0U,
+ },
+ .enable = {
+ .mask = GENMASK(15, 0),
+ .regoff = 0x4U,
+ },
+ .disable = {
+ .mask = GENMASK(15, 0),
+ .regoff = 0x8U,
+ },
+ .status = {
+ .mask = GENMASK(15, 0),
+ .regoff = 0xcU,
+ },
+ .regoff = 0x15000U,
+ .num_broadcasts = 0x10U,
+};
+
static u32 aie_get_tile_type(struct aie_location *loc)
{
if (loc->row)
@@ -476,6 +592,11 @@ int aie_device_init(struct aie_device *adev)
adev->col_rst = &aie_col_rst;
adev->col_clkbuf = &aie_col_clkbuf;
adev->shim_dma = &aie_shimdma;
+ adev->pl_events = &aie_pl_event;
+ adev->mem_events = &aie_mem_event;
+ adev->core_events = &aie_core_event;
+ adev->l1_ctrl = &aie_l1_intr_ctrl;
+ adev->l2_ctrl = &aie_l2_intr_ctrl;

/* Get the columns resource */
/* Get number of columns from AI engine memory resource */
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
index 78eae90..13abeca 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
@@ -14,6 +14,7 @@
#include <linux/firmware/xlnx-zynqmp.h>
#include <linux/fs.h>
#include <linux/idr.h>
+#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -402,6 +403,19 @@ static int xilinx_ai_engine_probe(struct platform_device *pdev)
of_xilinx_ai_engine_part_probe(adev);
dev_info(&pdev->dev, "Xilinx AI Engine device(cols=%u) probed\n",
adev->cols_res.total);
+
+ INIT_WORK(&adev->backtrack, aie_array_backtrack);
+
+ adev->irq = platform_get_irq_byname(pdev, "interrupt1");
+ if (adev->irq < 0)
+ goto free_ida;
+
+ ret = devm_request_threaded_irq(dev, adev->irq, NULL, aie_interrupt,
+ IRQF_ONESHOT, dev_name(dev), adev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request AIE IRQ.\n");
+ goto free_ida;
+ }
return 0;

free_ida:
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
index b21b7025..3b680f7 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
@@ -15,6 +15,7 @@
#include <linux/dma-buf.h>
#include <linux/file.h>
#include <linux/io.h>
+#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/of.h>
@@ -45,6 +46,55 @@
#define VERSAL_ES1_REV_ID 0x0
#define VERSAL_ES2_REV_ID 0x1

+#define AIE_NPI_ERROR_ID BIT(1)
+
+/* Macros relevant to interrupts */
+#define AIE_INTR_L2_CTRL_MASK_WIDTH 32
+
+/**
+ * enum aie_module_type - identifies different hardware modules within a
+ * tile type. AIE tile may have memory and core
+ * module. While a PL or shim tile may have PL module.
+ * @AIE_MEM_MOD: comprises of the following sub-modules,
+ * * data memory.
+ * * tile DMA.
+ * * lock module.
+ * * events, event broadcast and event actions.
+ * * tracing and profiling.
+ * @AIE_CORE_MOD: comprises of the following sub-modules,
+ * * AIE core.
+ * * program Memory.
+ * * events, event broadcast and event actions.
+ * * tracing and profiling.
+ * * AXI-MM and AXI-S tile interconnects.
+ * @AIE_PL_MOD: comprises of the following sub-modules,
+ * * PL interface.
+ * * AXI-MM and AXI-S tile interconnects.
+ * * Level 1 interrupt controllers.
+ * * events, event broadcast and event actions.
+ * * tracing and profiling.
+ * @AIE_NOC_MOD: comprises of the following sub-modules,
+ * * interface from NoC Slave Unit (NSU)
+ * (bridge to AXI-MM switch)
+ * * interfaces to NoC NoC Master Unit (NMU)
+ * * shim DMA & locks
+ * * NoC stream interface
+ */
+enum aie_module_type {
+ AIE_MEM_MOD,
+ AIE_CORE_MOD,
+ AIE_PL_MOD,
+ AIE_NOC_MOD,
+};
+
+/*
+ * enum aie_shim_switch_type - identifies different switches in shim tile.
+ */
+enum aie_shim_switch_type {
+ AIE_SHIM_SWITCH_A,
+ AIE_SHIM_SWITCH_B
+};
+
/**
* struct aie_tile_regs - contiguous range of AI engine register
* within an AI engine tile
@@ -157,6 +207,75 @@ struct aie_resource {
};

/**
+ * struct aie_event_attr - AI Engine event attributes structure.
+ * @bc_event: broadcast event attribute to capture event mask value and
+ * register offset from @bc_regoff.
+ * @group_error: group error attribute to capture error group mask value and
+ * register offset value from @group_regoff.
+ * @bc_regoff: base broadcast register offset.
+ * @status_regoff: base status register offset.
+ * @group_regoff: base group error register offset.
+ * @base_error_event: event ID of first error event in a group error.
+ * @num_broadcasts: total number of broadcast events.
+ * @base_bc_event: broadcast 0 vent ID
+ * @num_events: total number of events.
+ */
+struct aie_event_attr {
+ struct aie_single_reg_field bc_event;
+ struct aie_single_reg_field group_error;
+ u32 bc_regoff;
+ u32 status_regoff;
+ u32 group_regoff;
+ u32 base_error_event;
+ u32 num_broadcasts;
+ u32 base_bc_event;
+ u32 num_events;
+};
+
+/**
+ * struct aie_l1_intr_ctrl_attr - AI engine level 1 interrupt controller
+ * attributes structure.
+ * @mask: level 1 interrupt controller mask attribute.
+ * @swa_status: switch A level 1 interrupt controller status attribute.
+ * @swb_status: switch A level 1 interrupt controller status attribute.
+ * @swa_event: switch A level 1 interrupt controller event attribute.
+ * @swb_event: switch A level 1 interrupt controller event attribute.
+ * @regoff: base level 1 interrupt controller register offset.
+ * @event_lsb: lsb of IRQ event within IRQ event switch register.
+ * @num_broadcasts: total number of broadcast signals to level 1 interrupt
+ * controller.
+ */
+struct aie_l1_intr_ctrl_attr {
+ struct aie_single_reg_field swa_status;
+ struct aie_single_reg_field swb_status;
+ struct aie_single_reg_field swa_event;
+ struct aie_single_reg_field swb_event;
+ u32 regoff;
+ u32 event_lsb;
+ u32 num_broadcasts;
+};
+
+/**
+ * struct aie_l2_intr_ctrl_attr - AI engine level 2 interrupt controller
+ * attributes structure.
+ * @mask: level 2 interrupt controller mask attribute.
+ * @enable: level 2 interrupt controller enable attribute.
+ * @disable: level 2 interrupt controller disable attribute.
+ * @status: level 2 interrupt controller status attribute.
+ * @regoff: level 2 interrupt controller register offset.
+ * @num_broadcasts: total number of broadcast signals to level 2 interrupt
+ * controller.
+ */
+struct aie_l2_intr_ctrl_attr {
+ struct aie_single_reg_field mask;
+ struct aie_single_reg_field enable;
+ struct aie_single_reg_field disable;
+ struct aie_single_reg_field status;
+ u32 regoff;
+ u32 num_broadcasts;
+};
+
+/**
* struct aie_device - AI engine device structure
* @partitions: list of partitions requested
* @cdev: cdev for the AI engine
@@ -169,6 +288,11 @@ struct aie_resource {
* @col_rst: column reset attribute
* @col_clkbuf: column clock buffer attribute
* @shim_dma: SHIM DMA attribute
+ * @pl_events: pl module event attribute
+ * @mem_events: memory module event attribute
+ * @core_events: core module event attribute
+ * @l1_ctrl: level 1 interrupt controller attribute
+ * @l2_ctrl: level 2 interrupt controller attribute
* @size: size of the AI engine address space
* @array_shift: array address shift
* @col_shift: column address shift
@@ -176,6 +300,8 @@ struct aie_resource {
* @cols_res: AI engine columns resources to indicate
* while columns are occupied by partitions.
* @num_kernel_regs: number of kernel only registers range
+ * @irq: Linux IRQ number
+ * @backtrack: workqueue to backtrack interrupt
* @version: AI engine device version
* @pm_node_id: AI Engine platform management node ID
*/
@@ -191,12 +317,19 @@ struct aie_device {
const struct aie_single_reg_field *col_rst;
const struct aie_single_reg_field *col_clkbuf;
const struct aie_dma_attr *shim_dma;
+ const struct aie_event_attr *pl_events;
+ const struct aie_event_attr *mem_events;
+ const struct aie_event_attr *core_events;
+ const struct aie_l1_intr_ctrl_attr *l1_ctrl;
+ const struct aie_l2_intr_ctrl_attr *l2_ctrl;
size_t size;
struct aie_resource cols_res;
u32 array_shift;
u32 col_shift;
u32 row_shift;
u32 num_kernel_regs;
+ int irq;
+ struct work_struct backtrack;
int version;
u32 pm_node_id;
};
@@ -212,6 +345,7 @@ struct aie_device {
* @dev: device for the AI engine partition
* @cores_clk_state: bitmap to indicate the power state of core modules
* @tiles_inuse: bitmap to indicate if a tile is in use
+ * @l2_mask: level 2 interrupt controller mask bitmap
* @partition_id: partition id. Partition ID is the identifier
* of the AI engine partition in the system.
* @status: indicate if the partition is in use
@@ -228,6 +362,7 @@ struct aie_partition {
struct device dev;
struct aie_resource cores_clk_state;
struct aie_resource tiles_inuse;
+ struct aie_resource l2_mask;
u32 partition_id;
u32 status;
u32 cntrflag;
@@ -339,7 +474,12 @@ int aie_resource_get_region(struct aie_resource *res, u32 start,
void aie_resource_put_region(struct aie_resource *res, int start, u32 count);
int aie_resource_set(struct aie_resource *res, u32 start, u32 count);
int aie_resource_clear(struct aie_resource *res, u32 start, u32 count);
+int aie_resource_clear_all(struct aie_resource *res);
bool aie_resource_testbit(struct aie_resource *res, u32 bit);
+int aie_resource_cpy_from_arr32(struct aie_resource *res, u32 start,
+ const u32 *src, u32 nbits);
+int aie_resource_cpy_to_arr32(struct aie_resource *res, u32 start, u32 *dst,
+ u32 nbits);

const struct file_operations *aie_part_get_fops(void);
u8 aie_part_in_use(struct aie_partition *apart);
@@ -372,4 +512,8 @@ int aie_part_release_tiles_from_user(struct aie_partition *apart,
void __user *user_args);

int aie_device_init(struct aie_device *adev);
+
+void aie_array_backtrack(struct work_struct *work);
+irqreturn_t aie_interrupt(int irq, void *data);
+
#endif /* AIE_INTERNAL_H */
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-interrupt.c b/drivers/misc/xilinx-ai-engine/ai-engine-interrupt.c
new file mode 100644
index 0000000..9f98d72
--- /dev/null
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-interrupt.c
@@ -0,0 +1,661 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx AI Engine device driver
+ *
+ * Copyright (C) 2020 Xilinx, Inc.
+ */
+#include <linux/bitmap.h>
+#include <linux/firmware/xlnx-zynqmp.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include "ai-engine-internal.h"
+#include "linux/xlnx-ai-engine.h"
+
+#define AIE_ARRAY_TILE_ERROR_BC_ID 0U
+#define AIE_SHIM_TILE_ERROR_IRQ_ID 16U
+
+/**
+ * aie_get_broadcast_event() - get event ID being broadcast on given
+ * broadcast line.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @module: module type.
+ * @bc_id: broadcast ID.
+ * @return: event ID.
+ */
+static u8 aie_get_broadcast_event(struct aie_partition *apart,
+ struct aie_location *loc,
+ enum aie_module_type module, u8 bc_id)
+{
+ const struct aie_event_attr *event_mod;
+ u32 bcoff, regoff;
+
+ if (module == AIE_CORE_MOD)
+ event_mod = apart->adev->core_events;
+ else if (module == AIE_MEM_MOD)
+ event_mod = apart->adev->mem_events;
+ else
+ event_mod = apart->adev->pl_events;
+
+ bcoff = event_mod->bc_regoff + event_mod->bc_event.regoff + bc_id * 4U;
+ regoff = aie_cal_regoff(apart->adev, *loc, bcoff);
+ return ioread32(apart->adev->base + regoff);
+}
+
+/**
+ * aie_read_event_status() - get the status of event status registers.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @module: module type.
+ * @reg: array to store event status register values.
+ */
+static void aie_read_event_status(struct aie_partition *apart,
+ struct aie_location *loc,
+ enum aie_module_type module, u32 *reg)
+{
+ const struct aie_event_attr *event_mod;
+ u8 offset;
+
+ if (module == AIE_CORE_MOD)
+ event_mod = apart->adev->core_events;
+ else if (module == AIE_MEM_MOD)
+ event_mod = apart->adev->mem_events;
+ else
+ event_mod = apart->adev->pl_events;
+
+ for (offset = 0; offset < (event_mod->num_events / 32); offset++) {
+ u32 status_off = event_mod->status_regoff + offset * 4U;
+ u32 regoff = aie_cal_regoff(apart->adev, *loc, status_off);
+
+ reg[offset] = ioread32(apart->adev->base + regoff);
+ }
+}
+
+/**
+ * aie_check_group_errors_enabled() - get error events enabled in group error.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @module: module type.
+ * @return: bitmap of enabled error events.
+ */
+static u32 aie_check_group_errors_enabled(struct aie_partition *apart,
+ struct aie_location *loc,
+ enum aie_module_type module)
+{
+ const struct aie_event_attr *event_mod;
+ u32 groff, regoff;
+
+ if (module == AIE_CORE_MOD)
+ event_mod = apart->adev->core_events;
+ else if (module == AIE_MEM_MOD)
+ event_mod = apart->adev->mem_events;
+ else
+ event_mod = apart->adev->pl_events;
+
+ groff = event_mod->group_regoff + event_mod->group_error.regoff;
+ regoff = aie_cal_regoff(apart->adev, *loc, groff);
+ return ioread32(apart->adev->base + regoff);
+}
+
+/**
+ * aie_set_error_event() - enable/disable error events in group error.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @module: module type.
+ * @bitmap: error event to enable/disable in group errors.
+ */
+static void aie_set_error_event(struct aie_partition *apart,
+ struct aie_location *loc,
+ enum aie_module_type module, u32 bitmap)
+{
+ const struct aie_event_attr *event_mod;
+ u32 groff, regoff;
+
+ if (module == AIE_CORE_MOD)
+ event_mod = apart->adev->core_events;
+ else if (module == AIE_MEM_MOD)
+ event_mod = apart->adev->mem_events;
+ else
+ event_mod = apart->adev->pl_events;
+
+ groff = event_mod->group_regoff + event_mod->group_error.regoff;
+ regoff = aie_cal_regoff(apart->adev, *loc, groff);
+ iowrite32(bitmap, apart->adev->base + regoff);
+}
+
+/**
+ * aie_get_error_event() - map group error status bit to actual error
+ * event number.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @module: module type.
+ * @index: event index within group errors.
+ * @return: true event ID.
+ */
+static u32 aie_get_error_event(struct aie_partition *apart,
+ struct aie_location *loc,
+ enum aie_module_type module, u8 index)
+{
+ const struct aie_event_attr *event_mod;
+
+ if (module == AIE_CORE_MOD)
+ event_mod = apart->adev->core_events;
+ else if (module == AIE_MEM_MOD)
+ event_mod = apart->adev->mem_events;
+ else
+ event_mod = apart->adev->pl_events;
+
+ return event_mod->base_error_event + index;
+}
+
+/**
+ * aie_get_bc_event() - get the broadcast event ID.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @module: module type.
+ * @bc_id: broadcast line ID.
+ * @return: broadcast event ID.
+ */
+static u32 aie_get_bc_event(struct aie_partition *apart,
+ struct aie_location *loc,
+ enum aie_module_type module, u8 bc_id)
+{
+ const struct aie_event_attr *event_mod;
+
+ if (module == AIE_CORE_MOD)
+ event_mod = apart->adev->core_events;
+ else if (module == AIE_MEM_MOD)
+ event_mod = apart->adev->mem_events;
+ else
+ event_mod = apart->adev->pl_events;
+
+ return event_mod->base_bc_event + bc_id;
+}
+
+/**
+ * aie_get_l1_event() - get event ID being broadcast on level 1 IRQ.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @sw: switch type.
+ * @irq_id: IRQ event ID to be read.
+ * @return: true event ID.
+ */
+static u8 aie_get_l1_event(struct aie_partition *apart,
+ struct aie_location *loc,
+ enum aie_shim_switch_type sw, u8 irq_id)
+{
+ const struct aie_l1_intr_ctrl_attr *intr_ctrl = apart->adev->l1_ctrl;
+ u32 l1off, l1mask, regoff, reg_value;
+
+ if (sw == AIE_SHIM_SWITCH_A) {
+ l1off = intr_ctrl->regoff + intr_ctrl->swa_event.regoff;
+ l1mask = intr_ctrl->swa_event.mask;
+ } else {
+ l1off = intr_ctrl->regoff + intr_ctrl->swb_event.regoff;
+ l1mask = intr_ctrl->swb_event.mask;
+ }
+
+ regoff = aie_cal_regoff(apart->adev, *loc, l1off);
+ reg_value = ioread32(apart->adev->base + regoff);
+ reg_value &= l1mask << (irq_id * intr_ctrl->event_lsb);
+ reg_value >>= (irq_id * intr_ctrl->event_lsb);
+ return reg_value;
+}
+
+/**
+ * aie_clear_l1_intr() - clear level 1 interrupt controller status.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @sw: switch type.
+ * @irq_id: IRQ ID to be cleared.
+ */
+static void aie_clear_l1_intr(struct aie_partition *apart,
+ struct aie_location *loc,
+ enum aie_shim_switch_type sw, u8 irq_id)
+{
+ const struct aie_l1_intr_ctrl_attr *intr_ctrl = apart->adev->l1_ctrl;
+ u32 l1off, regoff;
+
+ if (sw == AIE_SHIM_SWITCH_A)
+ l1off = intr_ctrl->regoff + intr_ctrl->swa_status.regoff;
+ else
+ l1off = intr_ctrl->regoff + intr_ctrl->swb_status.regoff;
+
+ regoff = aie_cal_regoff(apart->adev, *loc, l1off);
+ iowrite32(BIT(irq_id), apart->adev->base + regoff);
+}
+
+/**
+ * aie_get_l1_status() - get level 1 interrupt controller status value.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @sw: switch type.
+ * @return: status value.
+ */
+static u32 aie_get_l1_status(struct aie_partition *apart,
+ struct aie_location *loc,
+ enum aie_shim_switch_type sw)
+{
+ const struct aie_l1_intr_ctrl_attr *intr_ctrl = apart->adev->l1_ctrl;
+ u32 l1off, regoff;
+
+ if (sw == AIE_SHIM_SWITCH_A)
+ l1off = intr_ctrl->regoff + intr_ctrl->swa_status.regoff;
+ else
+ l1off = intr_ctrl->regoff + intr_ctrl->swb_status.regoff;
+
+ regoff = aie_cal_regoff(apart->adev, *loc, l1off);
+ return ioread32(apart->adev->base + regoff);
+}
+
+/**
+ * aie_clear_l2_intr() - clear level 2 interrupt controller status.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @bitmap_irq: IRQ bitmap. IRQ lines corresponding to set bits will be
+ * cleared.
+ */
+static void aie_clear_l2_intr(struct aie_partition *apart,
+ struct aie_location *loc, u32 bitmap_irq)
+{
+ const struct aie_l2_intr_ctrl_attr *intr_ctrl = apart->adev->l2_ctrl;
+ u32 l2off = intr_ctrl->regoff + intr_ctrl->status.regoff;
+ u32 regoff = aie_cal_regoff(apart->adev, *loc, l2off);
+
+ iowrite32(bitmap_irq, apart->adev->base + regoff);
+}
+
+/**
+ * aie_get_l2_status() - get level 2 interrupt controller status value.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @return: status value.
+ */
+static u32 aie_get_l2_status(struct aie_partition *apart,
+ struct aie_location *loc)
+{
+ const struct aie_l2_intr_ctrl_attr *intr_ctrl = apart->adev->l2_ctrl;
+ u32 l2off = intr_ctrl->regoff + intr_ctrl->status.regoff;
+ u32 regoff = aie_cal_regoff(apart->adev, *loc, l2off);
+
+ return ioread32(apart->adev->base + regoff);
+}
+
+/**
+ * aie_get_l2_mask() - get level 2 interrupt controller mask value.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @return: mask value.
+ */
+static u32 aie_get_l2_mask(struct aie_partition *apart,
+ struct aie_location *loc)
+{
+ const struct aie_l2_intr_ctrl_attr *intr_ctrl = apart->adev->l2_ctrl;
+ u32 l2off = intr_ctrl->regoff + intr_ctrl->mask.regoff;
+ u32 regoff = aie_cal_regoff(apart->adev, *loc, l2off);
+
+ return ioread32(apart->adev->base + regoff);
+}
+
+/**
+ * aie_enable_l2_ctrl() - enable interrupts to level 2 interrupt controller.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @bit_map: bitmap of broadcast lines to enable.
+ */
+static void aie_enable_l2_ctrl(struct aie_partition *apart,
+ struct aie_location *loc, u32 bit_map)
+{
+ const struct aie_l2_intr_ctrl_attr *intr_ctrl = apart->adev->l2_ctrl;
+ u32 l2off = intr_ctrl->regoff + intr_ctrl->enable.regoff;
+ u32 regoff = aie_cal_regoff(apart->adev, *loc, l2off);
+
+ bit_map &= intr_ctrl->enable.mask;
+ iowrite32(bit_map, apart->adev->base + regoff);
+}
+
+/**
+ * aie_disable_l2_ctrl() - disable interrupts to level 2 interrupt controller.
+ * @apart: AIE partition pointer.
+ * @loc: pointer to tile location.
+ * @bit_map: bitmap of broadcast lines to disable.
+ */
+static void aie_disable_l2_ctrl(struct aie_partition *apart,
+ struct aie_location *loc, u32 bit_map)
+{
+ const struct aie_l2_intr_ctrl_attr *intr_ctrl = apart->adev->l2_ctrl;
+ u32 l2off = intr_ctrl->regoff + intr_ctrl->disable.regoff;
+ u32 regoff = aie_cal_regoff(apart->adev, *loc, l2off);
+
+ bit_map &= intr_ctrl->disable.mask;
+ iowrite32(bit_map, apart->adev->base + regoff);
+}
+
+/**
+ * aie_tile_backtrack() - if error was asserted on a broadcast line in
+ * the given array tile,
+ * * disable the error from the group errors
+ * @apart: AIE partition pointer.
+ * @loc: tile location.
+ * @module: module type.
+ * @sw: switch type.
+ * @bc_id: broadcast ID.
+ * @return: true if error was asserted, else return false.
+ */
+static bool aie_tile_backtrack(struct aie_partition *apart,
+ struct aie_location loc,
+ enum aie_module_type module,
+ enum aie_shim_switch_type sw, u8 bc_id)
+{
+ unsigned long grenabled;
+ u32 status[4];
+ u8 n, grevent, eevent;
+ bool ret = false;
+
+ if (module == AIE_PL_MOD)
+ grevent = aie_get_l1_event(apart, &loc, sw, bc_id);
+ else
+ grevent = aie_get_broadcast_event(apart, &loc, module, bc_id);
+
+ aie_read_event_status(apart, &loc, module, status);
+
+ if (!(status[grevent / 32] & BIT(grevent % 32)))
+ return ret;
+
+ grenabled = aie_check_group_errors_enabled(apart, &loc, module);
+ for_each_set_bit(n, &grenabled, 32) {
+ eevent = aie_get_error_event(apart, &loc, module, n);
+ if (!(status[eevent / 32] & BIT(eevent % 32)))
+ continue;
+ grenabled &= ~BIT(n);
+ ret = true;
+ dev_err_ratelimited(&apart->adev->dev,
+ "Asserted tile error event %d at col %d row %d\n",
+ eevent, loc.col, loc.row);
+ }
+ aie_set_error_event(apart, &loc, module, grenabled);
+
+ return ret;
+}
+
+/**
+ * aie_map_l2_to_l1() - map the status bit set in level 2 interrupt controller
+ * to a level 1 interrupt controller.
+ * @apart: AIE partition pointer.
+ * @set_pos: position of level 2 set bit.
+ * @l2_col: level 2 interrupt controller column ID.
+ * @l1_col: pointer to return corresponding level 1 column ID.
+ * @sw: pointer to return the level 1 interrupt controller switch ID.
+ *
+ * This API implementation is tightly coupled with the level 2 to level 1
+ * static mapping created when AIE application CDOs are generated.
+ */
+static void aie_map_l2_to_l1(struct aie_partition *apart, u32 set_pos,
+ u32 l2_col, u32 *l1_col,
+ enum aie_shim_switch_type *sw)
+{
+ if (l2_col + 3 >= apart->range.start.col + apart->range.size.col) {
+ *l1_col = l2_col + (set_pos % 6) / 2;
+ *sw = (set_pos % 6) % 2;
+ } else if (l2_col % 2 == 0) {
+ /* set bit position could be 0 - 5 */
+ *l1_col = l2_col - (2 - (set_pos % 6) / 2);
+ *sw = (set_pos % 6) % 2;
+ } else {
+ /* set bit position could be 0 - 1 */
+ *l1_col = l2_col;
+ *sw = set_pos;
+ }
+}
+
+/**
+ * aie_l1_backtrack() - backtrack AIE array tiles or shim tile based on
+ * the level 2 status bit set.
+ * @apart: AIE partition pointer.
+ * @loc: tile location of level 2 interrupt controller.
+ * @set_pos: set bit position in level 2 controller status.
+ * @return: true if error was asserted, else return false.
+ */
+static bool aie_l1_backtrack(struct aie_partition *apart,
+ struct aie_location loc, u32 set_pos)
+{
+ struct aie_location l1_ctrl;
+ enum aie_shim_switch_type sw;
+ unsigned long status;
+ u32 srow = apart->range.start.row + 1;
+ u32 erow = apart->range.start.row + apart->range.size.row;
+ bool ret = false;
+
+ /*
+ * Based on the set status bit find which level 1 interrupt
+ * controller has generated an interrupt
+ */
+ l1_ctrl.row = 0;
+ aie_map_l2_to_l1(apart, set_pos, loc.col, &l1_ctrl.col, &sw);
+
+ status = aie_get_l1_status(apart, &l1_ctrl, sw);
+
+ /* For now, support error broadcasts only */
+ if (status & BIT(AIE_ARRAY_TILE_ERROR_BC_ID)) {
+ struct aie_location temp;
+ enum aie_module_type module;
+ u32 bc_event;
+
+ if (sw == AIE_SHIM_SWITCH_A)
+ module = AIE_CORE_MOD;
+ else
+ module = AIE_MEM_MOD;
+
+ aie_clear_l1_intr(apart, &l1_ctrl, sw,
+ AIE_ARRAY_TILE_ERROR_BC_ID);
+
+ temp.row = srow;
+ temp.col = l1_ctrl.col;
+ bc_event = aie_get_bc_event(apart, &temp, module,
+ AIE_ARRAY_TILE_ERROR_BC_ID);
+ for (; temp.row < erow; temp.row++) {
+ u32 reg[4];
+
+ if (!aie_part_check_clk_enable_loc(apart, &temp))
+ break;
+
+ if (aie_tile_backtrack(apart, temp, module, sw,
+ AIE_ARRAY_TILE_ERROR_BC_ID))
+ ret = true;
+
+ aie_read_event_status(apart, &temp, module, reg);
+ if (!(reg[bc_event / 32] & BIT(bc_event % 32)))
+ break;
+ }
+ }
+
+ if (status & BIT(AIE_SHIM_TILE_ERROR_IRQ_ID)) {
+ aie_clear_l1_intr(apart, &l1_ctrl, sw,
+ AIE_SHIM_TILE_ERROR_IRQ_ID);
+ if (aie_tile_backtrack(apart, l1_ctrl, AIE_PL_MOD, sw,
+ AIE_SHIM_TILE_ERROR_IRQ_ID))
+ ret = true;
+ }
+ return ret;
+}
+
+/**
+ * aie_l2_backtrack() - iterate through each level 2 interrupt controller
+ * in a given partition and backtrack its
+ * corresponding level 1 interrupt controller.
+ * @apart: AIE partition pointer
+ */
+static void aie_l2_backtrack(struct aie_partition *apart)
+{
+ struct aie_location loc;
+ unsigned long l2_mask = 0;
+ u32 n, ttype, l2_bitmap_offset = 0;
+ int ret;
+ bool notify = false, sched_work = false;
+
+ ret = mutex_lock_interruptible(&apart->mlock);
+ if (ret) {
+ dev_err_ratelimited(&apart->dev,
+ "Failed to acquire lock. Process was interrupted by fatal signals\n");
+ return;
+ }
+
+ for (loc.col = apart->range.start.col, loc.row = 0;
+ loc.col < apart->range.start.col + apart->range.size.col;
+ loc.col++) {
+ ttype = apart->adev->ops->get_tile_type(&loc);
+ if (ttype != AIE_TILE_TYPE_SHIMNOC)
+ continue;
+
+ aie_resource_cpy_to_arr32(&apart->l2_mask, l2_bitmap_offset *
+ 32, (u32 *)&l2_mask, 32);
+ l2_bitmap_offset++;
+
+ for_each_set_bit(n, &l2_mask,
+ apart->adev->l2_ctrl->num_broadcasts) {
+ if (aie_l1_backtrack(apart, loc, n))
+ notify = true;
+ }
+
+ aie_enable_l2_ctrl(apart, &loc, l2_mask);
+ }
+
+ /*
+ * Level 2 interrupt registers are edge-triggered. As a result,
+ * re-enabling level 2 won't trigger an interrupt for the already
+ * latched interrupts at level 1 controller.
+ */
+ for (loc.col = apart->range.start.col, loc.row = 0;
+ loc.col < apart->range.start.col + apart->range.size.col;
+ loc.col++) {
+ if (aie_get_l1_status(apart, &loc, AIE_SHIM_SWITCH_A) ||
+ aie_get_l1_status(apart, &loc, AIE_SHIM_SWITCH_B)) {
+ mutex_unlock(&apart->mlock);
+ sched_work = true;
+ schedule_work(&apart->adev->backtrack);
+ break;
+ }
+ }
+
+ if (!sched_work)
+ mutex_unlock(&apart->mlock);
+}
+
+/**
+ * aie_part_backtrack() - backtrack a individual.
+ * @apart: AIE partition pointer.
+ */
+static void aie_part_backtrack(struct aie_partition *apart)
+{
+ aie_l2_backtrack(apart);
+}
+
+/**
+ * aie_array_backtrack() - backtrack each partition to find the source of error
+ * interrupt.
+ * @work: pointer to the work structure.
+ *
+ * This task will re-enable IRQ after errors in all partitions has been
+ * serviced.
+ */
+void aie_array_backtrack(struct work_struct *work)
+{
+ struct aie_device *adev;
+ struct aie_partition *apart;
+ int ret;
+
+ adev = container_of(work, struct aie_device, backtrack);
+
+ ret = mutex_lock_interruptible(&adev->mlock);
+ if (ret) {
+ dev_err_ratelimited(&adev->dev,
+ "Failed to acquire lock. Process was interrupted by fatal signals\n");
+ return;
+ }
+
+ list_for_each_entry(apart, &adev->partitions, node)
+ aie_part_backtrack(apart);
+
+ mutex_unlock(&adev->mlock);
+}
+
+/**
+ * aie_interrupt() - interrupt handler for AIE.
+ * @irq: Interrupt number.
+ * @data: AI engine device structure.
+ * @return: IRQ_HANDLED.
+ *
+ * This thread function disables level 2 interrupt controllers and schedules a
+ * task in workqueue to backtrack the source of error interrupt. Disabled
+ * interrupts are re-enabled after successful completion of bottom half.
+ */
+irqreturn_t aie_interrupt(int irq, void *data)
+{
+ struct aie_device *adev = data;
+ struct aie_partition *apart;
+ int ret;
+ bool sched_work = false;
+
+ ret = mutex_lock_interruptible(&adev->mlock);
+ if (ret) {
+ dev_err(&adev->dev,
+ "Failed to acquire lock. Process was interrupted by fatal signals\n");
+ return IRQ_NONE;
+ }
+
+ list_for_each_entry(apart, &adev->partitions, node) {
+ struct aie_location loc;
+ u32 ttype, l2_mask, l2_status, l2_bitmap_offset = 0;
+
+ ret = mutex_lock_interruptible(&apart->mlock);
+ if (ret) {
+ dev_err(&apart->dev,
+ "Failed to acquire lock. Process was interrupted by fatal signals\n");
+ return IRQ_NONE;
+ }
+
+ for (loc.col = apart->range.start.col, loc.row = 0;
+ loc.col < apart->range.start.col + apart->range.size.col;
+ loc.col++) {
+ ttype = apart->adev->ops->get_tile_type(&loc);
+ if (ttype != AIE_TILE_TYPE_SHIMNOC)
+ continue;
+
+ l2_mask = aie_get_l2_mask(apart, &loc);
+ if (l2_mask) {
+ aie_resource_cpy_from_arr32(&apart->l2_mask,
+ l2_bitmap_offset *
+ 32, &l2_mask, 32);
+ aie_disable_l2_ctrl(apart, &loc, l2_mask);
+ }
+ l2_bitmap_offset++;
+
+ l2_status = aie_get_l2_status(apart, &loc);
+ if (l2_status) {
+ aie_clear_l2_intr(apart, &loc, l2_status);
+ sched_work = true;
+ } else {
+ aie_enable_l2_ctrl(apart, &loc, l2_mask);
+ }
+ }
+ mutex_unlock(&apart->mlock);
+ }
+
+ /* For ES1 silicon, interrupts are latched in NPI */
+ if (adev->version == VERSAL_ES1_REV_ID) {
+ ret = zynqmp_pm_clear_aie_npi_isr(adev->pm_node_id,
+ AIE_NPI_ERROR_ID);
+ if (ret < 0)
+ dev_err(&adev->dev, "Failed to clear NPI ISR\n");
+ }
+
+ mutex_unlock(&adev->mlock);
+
+ if (sched_work)
+ schedule_work(&adev->backtrack);
+
+ return IRQ_HANDLED;
+}
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
index 54450b6..0670d0ad 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-part.c
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
@@ -247,6 +247,9 @@ static int aie_part_release(struct inode *inode, struct file *filp)
aie_part_clean(apart);

apart->status = 0;
+
+ aie_resource_clear_all(&apart->l2_mask);
+
mutex_unlock(&apart->mlock);

return 0;
@@ -369,6 +372,7 @@ static void aie_part_release_device(struct device *dev)
list_del(&apart->node);
mutex_unlock(&adev->mlock);
aie_resource_uninitialize(&apart->cores_clk_state);
+ aie_resource_uninitialize(&apart->l2_mask);
put_device(apart->dev.parent);
}

@@ -408,6 +412,39 @@ static int aie_part_create_mems_info(struct aie_partition *apart)
}

/**
+ * aie_part_create_l2_bitmap() - create bitmaps to record mask and status
+ * values for level 2 interrupt controllers.
+ * @apart: AI engine partition
+ * @return: 0 for success, and negative value for failure.
+ */
+static int aie_part_create_l2_bitmap(struct aie_partition *apart)
+{
+ struct aie_location loc;
+ u8 num_l2_ctrls = 0;
+ int ret;
+
+ loc.row = 0;
+ for (loc.col = apart->range.start.col;
+ loc.col < apart->range.start.col + apart->range.size.col;
+ loc.col++) {
+ u32 ttype = apart->adev->ops->get_tile_type(&loc);
+
+ if (ttype == AIE_TILE_TYPE_SHIMNOC)
+ num_l2_ctrls++;
+ }
+
+ ret = aie_resource_initialize(&apart->l2_mask, num_l2_ctrls *
+ AIE_INTR_L2_CTRL_MASK_WIDTH);
+ if (ret) {
+ dev_err(&apart->dev,
+ "failed to initialize l2 mask resource.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
* aie_create_partition() - create AI engine partition instance
* @adev: AI engine device
* @range: AI engine partition range to check. A range describes a group
@@ -498,6 +535,13 @@ static struct aie_partition *aie_create_partition(struct aie_device *adev,
return ERR_PTR(ret);
}

+ ret = aie_part_create_l2_bitmap(apart);
+ if (ret < 0) {
+ dev_err(&apart->dev, "Failed to allocate l2 bitmap.\n");
+ put_device(dev);
+ return ERR_PTR(ret);
+ }
+
ret = mutex_lock_interruptible(&adev->mlock);
if (ret) {
put_device(dev);
diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-res.c b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
index b0c0741..f1bb75b 100644
--- a/drivers/misc/xilinx-ai-engine/ai-engine-res.c
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
@@ -132,6 +132,44 @@ int aie_resource_set(struct aie_resource *res, u32 start, u32 count)
}

/**
+ * aie_resource_cpy_from_arr32() - copies nbits from u32[] to bitmap.
+ * @res: pointer to AI engine resource
+ * @start: start bit in bitmap
+ * @src: source buffer
+ * @nbits: number of bits to copy from u32[]
+ * @return: 0 for success and negative value for failure
+ */
+int aie_resource_cpy_from_arr32(struct aie_resource *res, u32 start,
+ const u32 *src, u32 nbits)
+{
+ if (!res || !res->bitmap || !nbits || start + nbits > res->total ||
+ !src)
+ return -EINVAL;
+
+ bitmap_from_arr32(res->bitmap + BIT_WORD(start), src, nbits);
+ return 0;
+}
+
+/**
+ * aie_resource_cpy_to_arr32() - copies nbits to u32[] from bitmap.
+ * @res: pointer to AI engine resource
+ * @start: start bit in bitmap
+ * @dst: destination buffer
+ * @nbits: number of bits to copy to u32[]
+ * @return: 0 for success and negative value for failure
+ */
+int aie_resource_cpy_to_arr32(struct aie_resource *res, u32 start, u32 *dst,
+ u32 nbits)
+{
+ if (!res || !res->bitmap || !nbits || start + nbits > res->total ||
+ !dst)
+ return -EINVAL;
+
+ bitmap_to_arr32(dst, res->bitmap + BIT_WORD(start), nbits);
+ return 0;
+}
+
+/**
* aie_resource_clear() - clear the AI engine resource bits
* @res: pointer to AI engine resource
* @start: start bit to set
@@ -150,6 +188,22 @@ int aie_resource_clear(struct aie_resource *res, u32 start, u32 count)
}

/**
+ * aie_resource_clear_all() - clear all the AI engine resource bits
+ * @res: pointer to AI engine resource
+ * @return: 0 for success and negative value for failure
+ *
+ * This function clears all the bits in the resource.
+ */
+int aie_resource_clear_all(struct aie_resource *res)
+{
+ if (!res || !res->bitmap)
+ return -EINVAL;
+
+ bitmap_clear(res->bitmap, 0, res->total);
+ return 0;
+}
+
+/**
* aie_resource_testbit() - test if a bit is set in a AI engine resource
* @res: pointer to AI engine resource
* @bit: bit to check
--
2.7.4

2020-12-08 20:59:53

by Wendy Liang

[permalink] [raw]
Subject: Re: [PATCH 2/9] misc: Add Xilinx AI engine device driver


On 12/8/20 9:12 AM, Nicolas Dufresne wrote:
> Le mercredi 18 novembre 2020 à 00:06 -0800, Wendy Liang a écrit :
>> Create AI engine device/partition hierarchical structure.
>>
>> Each AI engine device can have multiple logical partitions(groups of AI
>> engine tiles). Each partition is column based and has its own node ID
>> in the system. AI engine device driver manages its partitions.
>>
>> Applications can access AI engine partition through the AI engine
>> partition driver instance. AI engine registers write is moved to kernel
>> as there are registers in the AI engine array needs privilege
>> permission.
> Hi there, it's nice to see an effort to upstream an AI driver. I'm a little
> worried this driver is not obvious to use from it's source code itself. So you
> have reference to some Open Source code that demonstrate it's usage ?
We have AI engine library which provides a cross platforms APIs for other

libraries/application to use the hardware. Here is the source code:

https://github.com/Xilinx/embeddedsw/tree/master/XilinxProcessorIPLib/drivers/aienginev2/src


The cross platforms AI engine library runs in LInux userspace it defines
how to

configure, and the kernel driver controls if what can be access and
manage errors from device.


Best Regards,

Wendy
>
>> Signed-off-by: Wendy Liang <[email protected]>
>> Signed-off-by: Hyun Kwon <[email protected]>
>> ---
>>  MAINTAINERS                                        |   8 +
>>  drivers/misc/Kconfig                               |  12 +
>>  drivers/misc/Makefile                              |   1 +
>>  drivers/misc/xilinx-ai-engine/Makefile             |  11 +
>>  drivers/misc/xilinx-ai-engine/ai-engine-aie.c      | 115 +++++
>>  drivers/misc/xilinx-ai-engine/ai-engine-dev.c      | 448 ++++++++++++++++++
>>  drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 226 ++++++++++
>>  drivers/misc/xilinx-ai-engine/ai-engine-part.c     | 498
>> +++++++++++++++++++++
>>  drivers/misc/xilinx-ai-engine/ai-engine-res.c      | 114 +++++
>>  include/uapi/linux/xlnx-ai-engine.h                | 107 +++++
>>  10 files changed, 1540 insertions(+)
>>  create mode 100644 drivers/misc/xilinx-ai-engine/Makefile
>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-part.c
>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-res.c
>>  create mode 100644 include/uapi/linux/xlnx-ai-engine.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 5cc595a..40e3351 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -19283,6 +19283,14 @@ T:     git https://github.com/Xilinx/linux-xlnx.git
>>  F:     Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml
>>  F:     drivers/phy/xilinx/phy-zynqmp.c
>>
>> +XILINX AI ENGINE DRIVER
>> +M:     Wendy Liang <[email protected]>
>> +S:     Supported
>> +F:     Documentation/devicetree/bindings/soc/xilinx/xlnx,ai-engine.yaml
>> +F:     drivers/misc/xilinx-ai-engine/
>> +F:     include/linux/xlnx-ai-engine.h
>> +F:     include/uapi/linux/xlnx-ai-engine.h
>> +
>>  XILLYBUS DRIVER
>>  M:     Eli Billauer <[email protected]>
>>  L:     [email protected]
>> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
>> index fafa8b0..0b8ce4d 100644
>> --- a/drivers/misc/Kconfig
>> +++ b/drivers/misc/Kconfig
>> @@ -444,6 +444,18 @@ config XILINX_SDFEC
>>
>>           If unsure, say N.
>>
>> +config XILINX_AIE
>> +       tristate "Xilinx AI engine"
>> +       depends on ARM64 || COMPILE_TEST
>> +       help
>> +         This option enables support for the Xilinx AI engine driver.
>> +         One Xilinx AI engine device can have multiple partitions (groups of
>> +         AI engine tiles). Xilinx AI engine device driver instance manages
>> +         AI engine partitions. User application access its partitions through
>> +         AI engine partition instance file operations.
>> +
>> +         If unsure, say N
>> +
>>  config MISC_RTSX
>>         tristate
>>         default MISC_RTSX_PCI || MISC_RTSX_USB
>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>> index d23231e..2176b18 100644
>> --- a/drivers/misc/Makefile
>> +++ b/drivers/misc/Makefile
>> @@ -57,3 +57,4 @@ obj-$(CONFIG_HABANA_AI)               += habanalabs/
>>  obj-$(CONFIG_UACCE)            += uacce/
>>  obj-$(CONFIG_XILINX_SDFEC)     += xilinx_sdfec.o
>>  obj-$(CONFIG_HISI_HIKEY_USB)   += hisi_hikey_usb.o
>> +obj-$(CONFIG_XILINX_AIE)       += xilinx-ai-engine/
>> diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-
>> engine/Makefile
>> new file mode 100644
>> index 0000000..7827a0a
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/Makefile
>> @@ -0,0 +1,11 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +#
>> +# Makefile for Xilinx AI engine device driver
>> +#
>> +
>> +obj-$(CONFIG_XILINX_AIE)       += xilinx-aie.o
>> +
>> +xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \
>> +                                  ai-engine-dev.o \
>> +                                  ai-engine-part.o \
>> +                                  ai-engine-res.o
>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>> b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>> new file mode 100644
>> index 0000000..319260f
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>> @@ -0,0 +1,115 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Xilinx AI Engine driver AIE device specific implementation
>> + *
>> + * Copyright (C) 2020 Xilinx, Inc.
>> + */
>> +
>> +#include <linux/slab.h>
>> +
>> +#include "ai-engine-internal.h"
>> +
>> +#define AIE_ARRAY_SHIFT                30U
>> +#define AIE_COL_SHIFT          23U
>> +#define AIE_ROW_SHIFT          18U
>> +
>> +/*
>> + * Registers offsets
>> + */
>> +#define AIE_SHIMNOC_L2INTR_MASK_REGOFF         0x00015000U
>> +#define AIE_SHIMNOC_L2INTR_INTR_REGOFF         0x00015010U
>> +#define AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF     0x0001d000U
>> +#define AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF     0x0001d13cU
>> +#define AIE_SHIMNOC_AXIMM_REGOFF               0x0001e020U
>> +#define AIE_SHIMPL_L1INTR_MASK_A_REGOFF                0x00035000U
>> +#define AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF 0x00035050U
>> +#define AIE_SHIMPL_CLKCNTR_REGOFF              0x00036040U
>> +#define AIE_SHIMPL_RESET_REGOFF                        0x0003604cU
>> +#define AIE_TILE_CORE_CLKCNTR_REGOFF           0x00036040U
>> +
>> +static const struct aie_tile_regs aie_kernel_regs[] = {
>> +       /* SHIM AXI MM Config */
>> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMNOC_AXIMM_REGOFF,
>> +        .eoff = AIE_SHIMNOC_AXIMM_REGOFF,
>> +       },
>> +       /* SHIM DMA ADDRESS range */
>> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF,
>> +        .eoff = AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF,
>> +       },
>> +       /* SHIM 2nd level interrupt controller */
>> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMNOC_L2INTR_MASK_REGOFF,
>> +        .eoff = AIE_SHIMNOC_L2INTR_INTR_REGOFF,
>> +       },
>> +       /* SHIM 1st level interrupt controller */
>> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
>> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMPL_L1INTR_MASK_A_REGOFF,
>> +        .eoff = AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF,
>> +       },
>> +       /* SHIM reset Enable */
>> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
>> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMPL_RESET_REGOFF,
>> +        .eoff = AIE_SHIMPL_RESET_REGOFF,
>> +       },
>> +       /* SHIM clock control */
>> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
>> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMPL_CLKCNTR_REGOFF,
>> +        .eoff = AIE_SHIMPL_CLKCNTR_REGOFF,
>> +       },
>> +       /* Tile clock control */
>> +       {.attribute = AIE_TILE_TYPE_TILE << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_TILE_CORE_CLKCNTR_REGOFF,
>> +        .eoff = AIE_TILE_CORE_CLKCNTR_REGOFF,
>> +       },
>> +};
>> +
>> +static u32 aie_get_tile_type(struct aie_location *loc)
>> +{
>> +       if (loc->row)
>> +               return AIE_TILE_TYPE_TILE;
>> +       /* SHIM row */
>> +       if ((loc->col % 4) < 2)
>> +               return AIE_TILE_TYPE_SHIMPL;
>> +
>> +       return AIE_TILE_TYPE_SHIMNOC;
>> +}
>> +
>> +static const struct aie_tile_operations aie_ops = {
>> +       .get_tile_type = aie_get_tile_type,
>> +};
>> +
>> +/**
>> + * aie_device_init() - Initialize AI engine device struct AIE specific
>> + *                    properties
>> + * @adev: AI engine device
>> + * @return: 0 for success, negative value for failure.
>> + *
>> + * This function initialize the AI engine device structure device version
>> + * specific elements such as register addressing related array shift,
>> + * column shift, and row shift; AIE specific device operations, device
>> + * columns resource.
>> + */
>> +int aie_device_init(struct aie_device *adev)
>> +{
>> +       int ret;
>> +
>> +       adev->array_shift = AIE_ARRAY_SHIFT;
>> +       adev->col_shift = AIE_COL_SHIFT;
>> +       adev->row_shift = AIE_ROW_SHIFT;
>> +       adev->ops = &aie_ops;
>> +       adev->num_kernel_regs = ARRAY_SIZE(aie_kernel_regs);
>> +       adev->kernel_regs = aie_kernel_regs;
>> +
>> +       /* Get the columns resource */
>> +       /* Get number of columns from AI engine memory resource */
>> +       ret = aie_resource_initialize(&adev->cols_res, 50);
>> +       if (ret)
>> +               dev_err(&adev->dev, "failed to initialize columns
>> resource.\n");
>> +
>> +       return ret;
>> +}
>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>> b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>> new file mode 100644
>> index 0000000..2ab2dc8
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>> @@ -0,0 +1,448 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Xilinx AI Engine device driver
>> + *
>> + * Copyright (C) 2020 Xilinx, Inc.
>> + */
>> +
>> +#include <linux/anon_inodes.h>
>> +#include <linux/cdev.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/file.h>
>> +#include <linux/fs.h>
>> +#include <linux/idr.h>
>> +#include <linux/list.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/uaccess.h>
>> +#include <uapi/linux/xlnx-ai-engine.h>
>> +
>> +#include "ai-engine-internal.h"
>> +
>> +#define AIE_DEV_MAX    (MINORMASK + 1)
>> +
>> +static dev_t aie_major;
>> +struct class *aie_class;
>> +
>> +static DEFINE_IDA(aie_device_ida);
>> +static DEFINE_IDA(aie_minor_ida);
>> +
>> +/**
>> + * aie_get_partition_fd() - Get AI engine partition file descriptor
>> + * @apart: AI engine partition
>> + * @return: file descriptor for AI engine partition for success, or negative
>> + *         value for failure.
>> + *
>> + * This function gets a file descriptor for the AI engine partition.
>> + */
>> +static int aie_get_partition_fd(struct aie_partition *apart)
>> +{
>> +       struct file *filep;
>> +       int ret;
>> +
>> +       /*
>> +        * We can't use anon_inode_getfd() because we need to modify
>> +        * the f_mode flags directly to allow more than just ioctls
>> +        */
>> +       ret = get_unused_fd_flags(O_CLOEXEC);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       filep = anon_inode_getfile(dev_name(&apart->dev), &aie_part_fops,
>> +                                  apart, O_RDWR);
>> +       if (IS_ERR(filep)) {
>> +               put_unused_fd(ret);
>> +               ret = PTR_ERR(filep);
>> +               return ret;
>> +       }
>> +       filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
>> +       fd_install(ret, filep);
>> +
>> +       return ret;
>> +}
>> +
>> +/**
>> + * aie_enquire_partitions() - get AI engine partitions information
>> + * @adev: AI engine device
>> + * @query: data struct to store the partition information
>> + * @return: 0 for success, and negative value for failure.
>> + */
>> +static int aie_enquire_partitions(struct aie_device *adev,
>> +                                 struct aie_partition_query *query)
>> +{
>> +       struct aie_partition *apart;
>> +       u32 partition_cnt, i = 0;
>> +       int ret;
>> +
>> +       if (!query->partitions) {
>> +               /*
>> +                * If partitions information buffer is NULL.
>> +                * It is to get the number of partitions.
>> +                */
>> +               query->partition_cnt = 0;
>> +               list_for_each_entry(apart, &adev->partitions, node)
>> +                       query->partition_cnt++;
>> +               return 0;
>> +       }
>> +
>> +       partition_cnt = query->partition_cnt;
>> +       if (!partition_cnt)
>> +               return 0;
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret)
>> +               return ret;
>> +
>> +       list_for_each_entry(apart, &adev->partitions, node) {
>> +               struct aie_range_args part;
>> +
>> +               if (i >= partition_cnt)
>> +                       break;
>> +               part.partition_id = apart->partition_id;
>> +               /*
>> +                * TBD: check with PLM that if the partition is programmed
>> +                * and get the UID of the image which is loaded on the AI
>> +                * engine partition.
>> +                */
>> +               part.uid = 0;
>> +               part.range.start.col = apart->range.start.col;
>> +               part.range.start.row = apart->range.start.row;
>> +               part.range.size.col = apart->range.size.col;
>> +               part.range.size.row = apart->range.size.row;
>> +               /* Check if partition is in use */
>> +               part.status = apart->status;
>> +               if (copy_to_user((void __user *)&query->partitions[i], &part,
>> +                                sizeof(part))) {
>> +                       mutex_unlock(&adev->mlock);
>> +                       return -EFAULT;
>> +               }
>> +               i++;
>> +       }
>> +       mutex_unlock(&adev->mlock);
>> +       query->partition_cnt = i;
>> +
>> +       return 0;
>> +}
>> +
>> +/**
>> + * aie_get_partition_from_id() - get AI engine partition from id
>> + * @adev: AI engine device
>> + * @partition_id: partition id to check
>> + * @return: partition pointer if partition exists, otherwise, NULL.
>> + *
>> + * This function checks defined partitions with partition id.
>> + * This function expect the caller to lock mlock of @adev.
>> + */
>> +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
>> +                                               u32 partition_id)
>> +{
>> +       struct aie_partition *apart;
>> +
>> +       list_for_each_entry(apart, &adev->partitions, node) {
>> +               if (apart->partition_id == partition_id)
>> +                       return apart;
>> +       }
>> +
>> +       return NULL;
>> +}
>> +
>> +/**
>> + * aie_request_partition() - request AI engine partition
>> + * @adev: AI engine device
>> + * @req: partition request, includes the requested AI engine information
>> + *      such as partition node ID and the UID of the image which is
>> + *      loaded on the partition.
>> + * @return: partition pointer if partition exists, otherwise, NULL.
>> + *
>> + * This function finds a defined partition which matches the specified
>> + * partition id, request it if it hasn't been requested, and returns it.
>> + */
>> +struct aie_partition *aie_request_partition(struct aie_device *adev,
>> +                                           struct aie_partition_req *req)
>> +{
>> +       struct aie_partition *apart;
>> +       int ret;
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret)
>> +               return ERR_PTR(ret);
>> +
>> +       apart = aie_get_partition_from_id(adev, req->partition_id);
>> +       if (!apart) {
>> +               dev_err(&adev->dev,
>> +                       "request partition %u failed, not exist.\n",
>> +                       req->partition_id);
>> +               mutex_unlock(&adev->mlock);
>> +               return ERR_PTR(-EINVAL);
>> +       }
>> +       /*
>> +        * TBD: It will check image UID too to see if the user matches
>> +        * what's loaded in the AI engine partition. And check the meta
>> +        * data to see which resources used by application.
>> +        */
>> +
>> +       ret = mutex_lock_interruptible(&apart->mlock);
>> +       if (ret)
>> +               return ERR_PTR(ret);
>> +
>> +       if (apart->status & XAIE_PART_STATUS_INUSE) {
>> +               mutex_unlock(&apart->mlock);
>> +               dev_err(&adev->dev,
>> +                       "request partition %u failed, partition in use.\n",
>> +                       req->partition_id);
>> +               apart = ERR_PTR(-EBUSY);
>> +       } else {
>> +               /*
>> +                * TBD:
>> +                * 1. setup NOC AXI MM config to only generate error events
>> +                *    for slave error and decode error.
>> +                * 2. scan to see which tiles have been clock gated.
>> +                *
>> +                * This needs to be done before the AI engine partition is
>> +                * exported for user to access.
>> +                */
>> +               apart->status = XAIE_PART_STATUS_INUSE;
>> +               mutex_unlock(&apart->mlock);
>> +       }
>> +       mutex_unlock(&adev->mlock);
>> +
>> +       return apart;
>> +}
>> +
>> +static long xilinx_ai_engine_ioctl(struct file *filp, unsigned int cmd,
>> +                                  unsigned long arg)
>> +{
>> +       struct inode *inode = file_inode(filp);
>> +       struct aie_device *adev = cdev_to_aiedev(inode->i_cdev);
>> +       void __user *argp = (void __user *)arg;
>> +       int ret;
>> +
>> +       switch (cmd) {
>> +       case AIE_ENQUIRE_PART_IOCTL:
>> +       {
>> +               struct aie_partition_query query;
>> +               struct aie_partition_query  __user *uquery_ptr = argp;
>> +
>> +               if (copy_from_user(&query, uquery_ptr, sizeof(query)))
>> +                       return -EFAULT;
>> +               ret = aie_enquire_partitions(adev, &query);
>> +               if (ret < 0)
>> +                       return ret;
>> +               if (copy_to_user((void __user *)&uquery_ptr->partition_cnt,
>> +                                &query.partition_cnt,
>> +                                sizeof(query.partition_cnt)))
>> +                       return -EFAULT;
>> +               break;
>> +       }
>> +       case AIE_REQUEST_PART_IOCTL:
>> +       {
>> +               struct aie_partition_req req;
>> +               struct aie_partition *apart;
>> +
>> +               if (copy_from_user(&req, argp, sizeof(req)))
>> +                       return -EFAULT;
>> +               apart = aie_request_partition(adev, &req);
>> +               if (IS_ERR(apart))
>> +                       return PTR_ERR(apart);
>> +               ret = aie_get_partition_fd(apart);
>> +               if (ret < 0) {
>> +                       dev_err(&apart->dev, "failed to get fd.\n");
>> +                       break;
>> +               }
>> +               break;
>> +       }
>> +       default:
>> +               dev_err(&adev->dev, "Invalid ioctl command %u.\n", cmd);
>> +               ret = -EINVAL;
>> +               break;
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +static const struct file_operations aie_device_fops = {
>> +       .owner          = THIS_MODULE,
>> +       .unlocked_ioctl = xilinx_ai_engine_ioctl,
>> +};
>> +
>> +static void xilinx_ai_engine_release_device(struct device *dev)
>> +{
>> +       struct aie_device *adev = dev_to_aiedev(dev);
>> +
>> +       ida_simple_remove(&aie_device_ida, dev->id);
>> +       ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
>> +       cdev_del(&adev->cdev);
>> +       aie_resource_uninitialize(&adev->cols_res);
>> +}
>> +
>> +/**
>> + * of_xilinx_ai_engine_part_probe() - probes for AI engine partition nodes
>> + * @adev: AI engine device
>> + *
>> + * This function will probe for children AI engine partition nodes and create
>> + * an AI engine partition instance for each node.
>> + */
>> +static void of_xilinx_ai_engine_part_probe(struct aie_device *adev)
>> +{
>> +       struct device_node *nc;
>> +
>> +       for_each_available_child_of_node(adev->dev.of_node, nc) {
>> +               struct aie_partition *apart;
>> +
>> +               if (of_node_test_and_set_flag(nc, OF_POPULATED))
>> +                       continue;
>> +               apart = of_aie_part_probe(adev, nc);
>> +               if (IS_ERR(apart)) {
>> +                       dev_err(&adev->dev,
>> +                               "Failed to probe AI engine part for %pOF\n",
>> +                               nc);
>> +                       of_node_clear_flag(nc, OF_POPULATED);
>> +               }
>> +       }
>> +}
>> +
>> +static int xilinx_ai_engine_probe(struct platform_device *pdev)
>> +{
>> +       struct aie_device *adev;
>> +       struct device *dev;
>> +       int ret;
>> +
>> +       adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
>> +       if (!adev)
>> +               return -ENOMEM;
>> +       platform_set_drvdata(pdev, adev);
>> +       INIT_LIST_HEAD(&adev->partitions);
>> +       mutex_init(&adev->mlock);
>> +
>> +       adev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       if (!adev->res) {
>> +               dev_err(&pdev->dev, "No memory resource.\n");
>> +               return -EINVAL;
>> +       }
>> +       adev->base = devm_ioremap_resource(&pdev->dev, adev->res);
>> +       if (IS_ERR(adev->base)) {
>> +               dev_err(&pdev->dev, "no io memory resource.\n");
>> +               return PTR_ERR(adev->base);
>> +       }
>> +
>> +       /* Initialize AIE device specific instance. */
>> +       ret = aie_device_init(adev);
>> +       if (ret < 0) {
>> +               dev_err(&pdev->dev, "failed to initialize device
>> instance.\n");
>> +               return ret;
>> +       }
>> +
>> +       dev = &adev->dev;
>> +       device_initialize(dev);
>> +       dev->class = aie_class;
>> +       dev->parent = &pdev->dev;
>> +       dev->of_node = pdev->dev.of_node;
>> +
>> +       ret = ida_simple_get(&aie_minor_ida, 0, AIE_DEV_MAX, GFP_KERNEL);
>> +       if (ret < 0)
>> +               goto free_dev;
>> +       dev->devt = MKDEV(MAJOR(aie_major), ret);
>> +       ret = ida_simple_get(&aie_device_ida, 0, 0, GFP_KERNEL);
>> +       if (ret < 0)
>> +               goto free_minor_ida;
>> +       dev->id = ret;
>> +       dev_set_name(&adev->dev, "aie%d", dev->id);
>> +
>> +       cdev_init(&adev->cdev, &aie_device_fops);
>> +       adev->cdev.owner = THIS_MODULE;
>> +       ret = cdev_add(&adev->cdev, dev->devt, 1);
>> +       if (ret)
>> +               goto free_ida;
>> +       /* We can now rely on the release function for cleanup */
>> +       dev->release = xilinx_ai_engine_release_device;
>> +
>> +       ret = device_add(dev);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "device_add failed: %d\n", ret);
>> +               put_device(dev);
>> +               return ret;
>> +       }
>> +
>> +       of_xilinx_ai_engine_part_probe(adev);
>> +       dev_info(&pdev->dev, "Xilinx AI Engine device(cols=%u) probed\n",
>> +                adev->cols_res.total);
>> +       return 0;
>> +
>> +free_ida:
>> +       ida_simple_remove(&aie_device_ida, dev->id);
>> +free_minor_ida:
>> +       ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
>> +free_dev:
>> +       put_device(dev);
>> +
>> +       return ret;
>> +}
>> +
>> +static int xilinx_ai_engine_remove(struct platform_device *pdev)
>> +{
>> +       struct aie_device *adev = platform_get_drvdata(pdev);
>> +       struct aie_partition *apart;
>> +
>> +       list_for_each_entry(apart, &adev->partitions, node)
>> +               aie_part_remove(apart);
>> +
>> +       device_del(&adev->dev);
>> +       put_device(&adev->dev);
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id xilinx_ai_engine_of_match[] = {
>> +       { .compatible = "xlnx,ai-engine-v1.0", },
>> +       { /* end of table */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, xilinx_ai_engine_of_match);
>> +
>> +static struct platform_driver xilinx_ai_engine_driver = {
>> +       .probe                  = xilinx_ai_engine_probe,
>> +       .remove                 = xilinx_ai_engine_remove,
>> +       .driver                 = {
>> +               .name           = "xilinx-ai-engine",
>> +               .of_match_table = xilinx_ai_engine_of_match,
>> +       },
>> +};
>> +
>> +static int __init xilinx_ai_engine_init(void)
>> +{
>> +       int ret;
>> +
>> +       ret = alloc_chrdev_region(&aie_major, 0, AIE_DEV_MAX, "aie");
>> +       if (ret < 0) {
>> +               pr_err("aie: failed to allocate aie region\n");
>> +               return ret;
>> +       }
>> +
>> +       aie_class = class_create(THIS_MODULE, "aie");
>> +       if (IS_ERR(aie_class)) {
>> +               pr_err("failed to create aie class\n");
>> +               unregister_chrdev_region(aie_major, AIE_DEV_MAX);
>> +               return PTR_ERR(aie_class);
>> +       }
>> +
>> +       platform_driver_register(&xilinx_ai_engine_driver);
>> +
>> +       return 0;
>> +}
>> +postcore_initcall(xilinx_ai_engine_init);
>> +
>> +static void __exit xilinx_ai_engine_exit(void)
>> +{
>> +       platform_driver_unregister(&xilinx_ai_engine_driver);
>> +       class_destroy(aie_class);
>> +       unregister_chrdev_region(aie_major, AIE_DEV_MAX);
>> +}
>> +module_exit(xilinx_ai_engine_exit);
>> +
>> +MODULE_AUTHOR("Xilinx, Inc.");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>> b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>> new file mode 100644
>> index 0000000..6a69946
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>> @@ -0,0 +1,226 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Xilinx AI Engine driver internal header
>> + *
>> + * Copyright (C) 2020 Xilinx, Inc.
>> + */
>> +
>> +#ifndef AIE_INTERNAL_H
>> +#define AIE_INTERNAL_H
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/bits.h>
>> +#include <linux/cdev.h>
>> +#include <linux/device.h>
>> +#include <linux/io.h>
>> +#include <linux/list.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <uapi/linux/xlnx-ai-engine.h>
>> +
>> +/*
>> + * Macros for AI engine tile type bitmasks
>> + */
>> +#define AIE_TILE_TYPE_TILE     BIT(0)
>> +#define AIE_TILE_TYPE_SHIMPL   BIT(1)
>> +/* SHIM NOC tile includes SHIM PL and SHIM NOC modules */
>> +#define AIE_TILE_TYPE_SHIMNOC  BIT(2)
>> +
>> +/*
>> + * Macros for attribute property of AI engine registers accessed by kernel
>> + * 0 - 7 bits: tile type bits
>> + * 8 - 15 bits: permission bits. If it is 1, it allows write from userspace
>> + */
>> +#define AIE_REGS_ATTR_TILE_TYPE_SHIFT  0U
>> +#define AIE_REGS_ATTR_PERM_SHIFT       8U
>> +#define AIE_REGS_ATTR_TILE_TYPE_MASK   GENMASK(AIE_REGS_ATTR_PERM_SHIFT - 1,
>> \
>> +                                               AIE_REGS_ATTR_TILE_TYPE_SHIFT)
>> +#define AIE_REGS_ATTR_PERM_MASK                GENMASK(15, \
>> +                                               AIE_REGS_ATTR_PERM_SHIFT)
>> +
>> +/**
>> + * struct aie_tile_regs - contiguous range of AI engine register
>> + *                       within an AI engine tile
>> + * @soff: start offset of the range
>> + * @eoff: end offset of the range
>> + * @attribute: registers attribute. It uses AIE_REGS_ATTR_* macros defined
>> + *            above.
>> + */
>> +struct aie_tile_regs {
>> +       size_t soff;
>> +       size_t eoff;
>> +       u32 attribute;
>> +};
>> +
>> +struct aie_device;
>> +struct aie_partition;
>> +
>> +/**
>> + * struct aie_tile_operations - AI engine device operations
>> + * @get_tile_type: get type of tile based on tile operation
>> + *
>> + * Different AI engine device version has its own device
>> + * operation.
>> + */
>> +struct aie_tile_operations {
>> +       u32 (*get_tile_type)(struct aie_location *loc);
>> +};
>> +
>> +/**
>> + * struct aie_resource - AI engine resource structure
>> + * @bitmap: resource bitmap
>> + * @total: total number of resource
>> + */
>> +struct aie_resource {
>> +       unsigned long *bitmap;
>> +       u32 total;
>> +};
>> +
>> +/**
>> + * struct aie_device - AI engine device structure
>> + * @partitions: list of partitions requested
>> + * @cdev: cdev for the AI engine
>> + * @dev: device for the AI engine device
>> + * @mlock: protection for AI engine device operations
>> + * @base: AI engine device base virtual address
>> + * @res: memory resource of AI engine device
>> + * @kernel_regs: array of kernel only registers
>> + * @ops: tile operations
>> + * @size: size of the AI engine address space
>> + * @array_shift: array address shift
>> + * @col_shift: column address shift
>> + * @row_shift: row address shift
>> + * @cols_res: AI engine columns resources to indicate
>> + *           while columns are occupied by partitions.
>> + * @num_kernel_regs: number of kernel only registers range
>> + * @version: AI engine device version
>> + */
>> +struct aie_device {
>> +       struct list_head partitions;
>> +       struct cdev cdev;
>> +       struct device dev;
>> +       struct mutex mlock; /* protection for AI engine partitions */
>> +       void __iomem *base;
>> +       struct resource *res;
>> +       const struct aie_tile_regs *kernel_regs;
>> +       const struct aie_tile_operations *ops;
>> +       size_t size;
>> +       struct aie_resource cols_res;
>> +       u32 array_shift;
>> +       u32 col_shift;
>> +       u32 row_shift;
>> +       u32 num_kernel_regs;
>> +       int version;
>> +};
>> +
>> +/**
>> + * struct aie_partition - AI engine partition structure
>> + * @node: list node
>> + * @adev: pointer to AI device instance
>> + * @range: range of partition
>> + * @mlock: protection for AI engine partition operations
>> + * @dev: device for the AI engine partition
>> + * @partition_id: partition id. Partition ID is the identifier
>> + *               of the AI engine partition in the system.
>> + * @status: indicate if the partition is in use
>> + */
>> +struct aie_partition {
>> +       struct list_head node;
>> +       struct aie_device *adev;
>> +       struct aie_range range;
>> +       struct mutex mlock; /* protection for AI engine partition operations
>> */
>> +       struct device dev;
>> +       u32 partition_id;
>> +       u32 status;
>> +};
>> +
>> +extern struct class *aie_class;
>> +extern const struct file_operations aie_part_fops;
>> +
>> +#define cdev_to_aiedev(i_cdev) container_of((i_cdev), struct aie_device,
>> cdev)
>> +#define dev_to_aiedev(_dev) container_of((_dev), struct aie_device, dev)
>> +#define dev_to_aiepart(_dev) container_of((_dev), struct aie_partition, dev)
>> +
>> +#define aie_col_mask(adev) ({ \
>> +       struct aie_device *_adev = (adev); \
>> +       GENMASK_ULL(_adev->array_shift - 1, _adev->col_shift);  \
>> +       })
>> +
>> +#define aie_row_mask(adev) ({ \
>> +       struct aie_device *_adev = (adev); \
>> +       GENMASK_ULL(_adev->col_shift - 1, _adev->row_shift);  \
>> +       })
>> +
>> +#define aie_tile_reg_mask(adev) ({ \
>> +       struct aie_device *_adev = (adev); \
>> +       GENMASK_ULL(_adev->row_shift - 1, 0);  \
>> +       })
>> +
>> +/*
>> + * Need to define field get, as AI engine shift mask is not constant.
>> + * Cannot use FIELD_GET()
>> + */
>> +#define aie_tile_reg_field_get(mask, shift, regoff) ( \
>> +       ((regoff) & (mask)) >> (shift))
>> +
>> +#define aie_cal_tile_reg(adev, regoff) ( \
>> +       aie_tile_reg_field_get(aie_tile_reg_mask(adev), 0, regoff))
>> +
>> +/**
>> + * aie_cal_regoff() - calculate register offset to the whole AI engine
>> + *                   device start address
>> + * @adev: AI engine device
>> + * @loc: AI engine tile location
>> + * @regoff_intile: register offset within a tile
>> + * @return: register offset to the whole AI engine device start address
>> + */
>> +static inline u32 aie_cal_regoff(struct aie_device *adev,
>> +                                struct aie_location loc, u32 regoff_intile)
>> +{
>> +       return regoff_intile + (loc.col << adev->col_shift) +
>> +              (loc.row << adev->row_shift);
>> +}
>> +
>> +/**
>> + * aie_validate_location() - validate tile location within an AI engine
>> + *                          partition
>> + * @apart: AI engine partition
>> + * @loc: AI engine tile location
>> + * @return: return 0 if it is valid, negative value for errors.
>> + *
>> + * This function checks if the AI engine location is within the AI engine
>> + * partition.
>> + */
>> +static inline int aie_validate_location(struct aie_partition *apart,
>> +                                       struct aie_location loc)
>> +{
>> +       if (loc.col < apart->range.start.col ||
>> +           loc.col >= apart->range.start.col + apart->range.size.col ||
>> +           loc.row < apart->range.start.row ||
>> +           loc.row >= apart->range.start.row + apart->range.size.row)
>> +               return -EINVAL;
>> +
>> +       return 0;
>> +}
>> +
>> +int aie_resource_initialize(struct aie_resource *res, int count);
>> +void aie_resource_uninitialize(struct aie_resource *res);
>> +int aie_resource_check_region(struct aie_resource *res, u32 start,
>> +                             u32 count);
>> +int aie_resource_get_region(struct aie_resource *res, u32 start,
>> +                           u32 count);
>> +void aie_resource_put_region(struct aie_resource *res, int start, u32 count);
>> +
>> +const struct file_operations *aie_part_get_fops(void);
>> +u8 aie_part_in_use(struct aie_partition *apart);
>> +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
>> +                                               u32 partition_id);
>> +struct aie_partition *aie_request_partition(struct aie_device *adev,
>> +                                           struct aie_partition_req *req);
>> +struct aie_partition *of_aie_part_probe(struct aie_device *adev,
>> +                                       struct device_node *nc);
>> +void aie_part_remove(struct aie_partition *apart);
>> +
>> +int aie_device_init(struct aie_device *adev);
>> +#endif /* AIE_INTERNAL_H */
>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c
>> b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
>> new file mode 100644
>> index 0000000..fc8f9f5
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
>> @@ -0,0 +1,498 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Xilinx AI Engine partition driver
>> + *
>> + * Copyright (C) 2020 Xilinx, Inc.
>> + */
>> +
>> +#include <linux/cdev.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/fs.h>
>> +#include <linux/kernel.h>
>> +#include <linux/list.h>
>> +#include <linux/mm.h>
>> +#include <linux/mman.h>
>> +#include <linux/mmu_context.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/uio.h>
>> +#include <uapi/linux/xlnx-ai-engine.h>
>> +
>> +#include "ai-engine-internal.h"
>> +
>> +/**
>> + * aie_cal_loc() - calculate tile location from register offset to the AI
>> + *                engine device
>> + * @adev: AI engine device
>> + * @loc: memory pointer to restore returning location information
>> + * @regoff: tile internal register offset
>> + *
>> + * This function returns the tile location.
>> + */
>> +static void aie_cal_loc(struct aie_device *adev,
>> +                       struct aie_location *loc, u64 regoff)
>> +{
>> +       loc->col = (u32)aie_tile_reg_field_get(aie_col_mask(adev),
>> +                                              adev->col_shift, regoff);
>> +       loc->row = (u32)aie_tile_reg_field_get(aie_row_mask(adev),
>> +                                              adev->row_shift, regoff);
>> +}
>> +
>> +/**
>> + * aie_part_reg_validation() - validate AI engine partition register access
>> + * @apart: AI engine partition
>> + * @offset: AI engine register offset
>> + * @len: len of data to write/read
>> + * @is_write: is the access to write to register
>> + * @return: 0 for success, or negative value for failure.
>> + *
>> + * This function validate if the register to access is within the AI engine
>> + * partition. If it is write access, if the register is writable by user.
>> + */
>> +static int aie_part_reg_validation(struct aie_partition *apart, size_t
>> offset,
>> +                                  size_t len, u8 is_write)
>> +{
>> +       struct aie_device *adev;
>> +       u32 regend32, ttype;
>> +       u64 regoff, regend64;
>> +       struct aie_location loc;
>> +       unsigned int i;
>> +
>> +       adev = apart->adev;
>> +       if (offset % sizeof(u32)) {
>> +               dev_err(&apart->dev,
>> +                       "Invalid reg off(0x%zx), not 32bit aligned.\n",
>> +                       offset);
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (len % sizeof(u32)) {
>> +               dev_err(&apart->dev, "Invalid reg operation len %zu.\n", len);
>> +               return -EINVAL;
>> +       }
>> +
>> +       regoff = aie_cal_tile_reg(adev, offset);
>> +       regend64 = regoff + len;
>> +       if (regend64 >= BIT_ULL(adev->row_shift)) {
>> +               dev_err(&apart->dev,
>> +                       "Invalid reg operation len %zu.\n", len);
>> +               return -EINVAL;
>> +       }
>> +
>> +       aie_cal_loc(adev, &loc, offset);
>> +       if (aie_validate_location(apart, loc)) {
>> +               dev_err(&apart->dev,
>> +                       "Invalid (%d,%d) out of part(%d,%d),(%d,%d)\n",
>> +                       loc.col, loc.row,
>> +                       apart->range.start.col, apart->range.start.row,
>> +                       apart->range.size.col, apart->range.size.row);
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (!is_write)
>> +               return 0;
>> +
>> +       regend32 = lower_32_bits(regend64);
>> +       ttype = adev->ops->get_tile_type(&loc);
>> +       for (i = 0; i < adev->num_kernel_regs; i++) {
>> +               const struct aie_tile_regs *regs;
>> +               u32 rttype, writable;
>> +
>> +               regs = &adev->kernel_regs[i];
>> +               rttype = (regs->attribute & AIE_REGS_ATTR_TILE_TYPE_MASK) >>
>> +                        AIE_REGS_ATTR_TILE_TYPE_SHIFT;
>> +               writable = (regs->attribute & AIE_REGS_ATTR_PERM_MASK) >>
>> +                          AIE_REGS_ATTR_PERM_SHIFT;
>> +               if (!(ttype & rttype))
>> +                       continue;
>> +               if ((regoff >= regs->soff && regoff <= regs->eoff) ||
>> +                   (regend32 >= regs->soff && regend32 <= regs->eoff)) {
>> +                       if (!writable) {
>> +                               dev_err(&apart->dev,
>> +                                       "reg 0x%zx,0x%zx not writable.\n",
>> +                                       offset, len);
>> +                               return -EINVAL;
>> +                       }
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +/**
>> + * aie_part_write_register() - AI engine partition write register
>> + * @apart: AI engine partition
>> + * @offset: AI engine register offset
>> + * @len: len of data to write
>> + * @data: data to write
>> + * @mask: mask, if it is non 0, it is mask write.
>> + * @return: number of bytes write for success, or negative value for failure.
>> + *
>> + * This function writes data to the specified registers.
>> + * If the mask is non 0, it is mask write.
>> + */
>> +static int aie_part_write_register(struct aie_partition *apart, size_t
>> offset,
>> +                                  size_t len, void *data, u32 mask)
>> +{
>> +       int ret;
>> +       void __iomem *va;
>> +
>> +       if (mask && len > sizeof(u32)) {
>> +               /* For mask write, only allow 32bit. */
>> +               dev_err(&apart->dev,
>> +                       "failed mask write, len is more that 32bit.\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       /* offset is expected to be relative to the start of the partition */
>> +       offset += aie_cal_regoff(apart->adev, apart->range.start, 0);
>> +       ret = aie_part_reg_validation(apart, offset, len, 1);
>> +       if (ret < 0) {
>> +               dev_err(&apart->dev, "failed to write to 0x%zx,0x%zx.\n",
>> +                       offset, len);
>> +               return ret;
>> +       }
>> +
>> +       va = apart->adev->base + offset;
>> +       if (!mask) {
>> +               if (len == sizeof(u32))
>> +                       iowrite32(*((u32 *)data),  va);
>> +               else
>> +                       memcpy_toio(va, data, len);
>> +       } else {
>> +               u32 val = ioread32(va);
>> +
>> +               val &= ~mask;
>> +               val |= *((u32 *)data) & mask;
>> +               iowrite32(val, va);
>> +       }
>> +
>> +       return (int)len;
>> +}
>> +
>> +/**
>> + * aie_part_access_regs() - AI engine partition registers access
>> + * @apart: AI engine partition
>> + * @num_reqs: number of access requests
>> + * @reqs: array of registers access
>> + * @return: 0 for success, and negative value for failure.
>> + *
>> + * This function executes AI engine partition register access requests.
>> + */
>> +static int aie_part_access_regs(struct aie_partition *apart, u32 num_reqs,
>> +                               struct aie_reg_args *reqs)
>> +{
>> +       u32 i;
>> +
>> +       for (i = 0; i < num_reqs; i++) {
>> +               struct aie_reg_args *args = &reqs[i];
>> +               int ret;
>> +
>> +               if (args->op != AIE_REG_WRITE) {
>> +                       dev_err(&apart->dev,
>> +                               "Invalid register command type: %u.\n",
>> +                               args->op);
>> +                       return -EINVAL;
>> +               }
>> +               ret = aie_part_write_register(apart,
>> +                                             (size_t)args->offset,
>> +                                             sizeof(args->val),
>> +                                             &args->val, args->mask);
>> +               if (ret < 0) {
>> +                       dev_err(&apart->dev, "reg op %u failed: 0x%llx.\n",
>> +                               args->op, args->offset);
>> +                       return ret;
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int aie_part_release(struct inode *inode, struct file *filp)
>> +{
>> +       struct aie_partition *apart = filp->private_data;
>> +       int ret;
>> +
>> +       /*
>> +        * TODO: It will need to reset the SHIM columns and gate the
>> +        * tiles of the partition.
>> +        */
>> +       ret = mutex_lock_interruptible(&apart->mlock);
>> +       if (ret)
>> +               return ret;
>> +
>> +       apart->status = 0;
>> +       mutex_unlock(&apart->mlock);
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct vm_operations_struct aie_part_physical_vm_ops = {
>> +#ifdef CONFIG_HAVE_IOREMAP_PROT
>> +       .access = generic_access_phys,
>> +#endif
>> +};
>> +
>> +static int aie_part_mmap(struct file *fp, struct vm_area_struct *vma)
>> +{
>> +       struct aie_partition *apart = fp->private_data;
>> +       struct aie_device *adev = apart->adev;
>> +       unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
>> +       phys_addr_t addr;
>> +       size_t size;
>> +
>> +       if (vma->vm_end < vma->vm_start)
>> +               return -EINVAL;
>> +       /* Only allow userspace directly read registers */
>> +       if (vma->vm_flags & VM_WRITE) {
>> +               dev_err(&apart->dev, "%s: do not support writable mmap.\n",
>> +                       __func__);
>> +               return -EINVAL;
>> +       }
>> +       vma->vm_private_data = apart;
>> +       vma->vm_ops = &aie_part_physical_vm_ops;
>> +       size = apart->range.size.col << adev->col_shift;
>> +       if ((vma->vm_end - vma->vm_start) > (size - offset)) {
>> +               dev_err(&apart->dev,
>> +                       "%s: size exceed.\n", __func__);
>> +               return -EINVAL;
>> +       }
>> +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
>> +       /* Calculate the partition address */
>> +       addr = adev->res->start;
>> +       addr += apart->range.start.col << adev->col_shift;
>> +       addr += apart->range.start.row << adev->row_shift;
>> +       addr += offset;
>> +       return remap_pfn_range(vma,
>> +                              vma->vm_start,
>> +                              addr >> PAGE_SHIFT,
>> +                              vma->vm_end - vma->vm_start,
>> +                              vma->vm_page_prot);
>> +}
>> +
>> +static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long
>> arg)
>> +{
>> +       struct aie_partition *apart = fp->private_data;
>> +       void __user *argp = (void __user *)arg;
>> +       long ret;
>> +
>> +       switch (cmd) {
>> +       case AIE_REG_IOCTL:
>> +       {
>> +               struct aie_reg_args raccess;
>> +
>> +               if (copy_from_user(&raccess, argp, sizeof(raccess)))
>> +                       return -EFAULT;
>> +
>> +               ret = mutex_lock_interruptible(&apart->mlock);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               ret = aie_part_access_regs(apart, 1, &raccess);
>> +               mutex_unlock(&apart->mlock);
>> +               break;
>> +       }
>> +       default:
>> +               dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd);
>> +               ret = -EINVAL;
>> +               break;
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +const struct file_operations aie_part_fops = {
>> +       .owner          = THIS_MODULE,
>> +       .release        = aie_part_release,
>> +       .mmap           = aie_part_mmap,
>> +       .unlocked_ioctl = aie_part_ioctl,
>> +};
>> +
>> +/**
>> + * aie_part_release_device() - release an AI engine partition instance
>> + * @dev: AI engine partition device
>> + *
>> + * It will be called by device driver core when no one holds a valid
>> + * pointer to @dev anymore.
>> + */
>> +static void aie_part_release_device(struct device *dev)
>> +{
>> +       struct aie_partition *apart = dev_to_aiepart(dev);
>> +       struct aie_device *adev = apart->adev;
>> +       int ret;
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret) {
>> +               dev_warn(&apart->dev,
>> +                        "getting adev->mlock is interrupted by signal\n");
>> +       }
>> +
>> +       aie_resource_put_region(&adev->cols_res, apart->range.start.col,
>> +                               apart->range.size.col);
>> +       list_del(&apart->node);
>> +       mutex_unlock(&adev->mlock);
>> +       put_device(apart->dev.parent);
>> +}
>> +
>> +/**
>> + * aie_create_partition() - create AI engine partition instance
>> + * @adev: AI engine device
>> + * @range: AI engine partition range to check. A range describes a group
>> + *        of AI engine tiles.
>> + * @return: created AI engine partition pointer for success, and PTR_ERR
>> + *         for failure.
>> + *
>> + * This function creates an AI engine partition instance.
>> + * It creates AI engine partition, the AI engine partition device and
>> + * the AI engine partition character device.
>> + */
>> +static struct aie_partition *aie_create_partition(struct aie_device *adev,
>> +                                                 struct aie_range *range)
>> +{
>> +       struct aie_partition *apart;
>> +       struct device *dev;
>> +       char devname[32];
>> +       int ret;
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret)
>> +               return ERR_PTR(ret);
>> +
>> +       ret = aie_resource_check_region(&adev->cols_res, range->start.col,
>> +                                       range->size.col);
>> +       if (ret != range->start.col) {
>> +               dev_err(&adev->dev, "invalid partition (%u,%u)(%u,%u).\n",
>> +                       range->start.col, range->start.row,
>> +                       range->size.col, range->size.row);
>> +               mutex_unlock(&adev->mlock);
>> +               return ERR_PTR(-EINVAL);
>> +       }
>> +       ret = aie_resource_get_region(&adev->cols_res, range->start.col,
>> +                                     range->size.col);
>> +       if (ret != range->start.col) {
>> +               dev_err(&adev->dev, "failed to get partition
>> (%u,%u)(%u,%u).\n",
>> +                       range->start.col, range->start.row,
>> +                       range->size.col, range->size.row);
>> +               mutex_unlock(&adev->mlock);
>> +               return ERR_PTR(-EFAULT);
>> +       }
>> +       mutex_unlock(&adev->mlock);
>> +
>> +       apart = devm_kzalloc(&adev->dev, sizeof(*apart), GFP_KERNEL);
>> +       if (!apart)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       apart->adev = adev;
>> +       memcpy(&apart->range, range, sizeof(*range));
>> +       mutex_init(&apart->mlock);
>> +
>> +       /* Create AI engine partition device */
>> +       dev = &apart->dev;
>> +       device_initialize(dev);
>> +       dev->parent = &adev->dev;
>> +       dev->class = aie_class;
>> +       dev_set_drvdata(dev, apart);
>> +       snprintf(devname, sizeof(devname) - 1, "aiepart_%d_%d",
>> +                apart->range.start.col, apart->range.size.col);
>> +       dev_set_name(dev, devname);
>> +       /* We can now rely on the release function for cleanup */
>> +       dev->release = aie_part_release_device;
>> +       ret = device_add(dev);
>> +       if (ret) {
>> +               dev_err(dev, "device_add failed: %d\n", ret);
>> +               put_device(dev);
>> +               return ERR_PTR(ret);
>> +       }
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret) {
>> +               put_device(dev);
>> +               return ERR_PTR(ret);
>> +       }
>> +
>> +       list_add_tail(&apart->node, &adev->partitions);
>> +       mutex_unlock(&adev->mlock);
>> +       get_device(&adev->dev);
>> +       dev_dbg(dev, "created AIE partition device.\n");
>> +
>> +       return apart;
>> +}
>> +
>> +struct aie_partition *
>> +of_aie_part_probe(struct aie_device *adev, struct device_node *nc)
>> +{
>> +       struct aie_partition *apart;
>> +       struct aie_range range;
>> +       u32 partition_id, regs[4];
>> +       int ret;
>> +
>> +       /* Select device driver */
>> +       ret = of_property_read_u32_array(nc, "reg", regs, ARRAY_SIZE(regs));
>> +       if (ret < 0) {
>> +               dev_err(&adev->dev,
>> +                       "probe %pOF failed, no tiles range information.\n",
>> +                       nc);
>> +               return ERR_PTR(ret);
>> +       }
>> +       range.start.col = regs[0];
>> +       range.start.row = regs[1];
>> +       range.size.col = regs[2];
>> +       range.size.row = regs[3];
>> +
>> +       ret = of_property_read_u32_index(nc, "xlnx,partition-id", 0,
>> +                                        &partition_id);
>> +       if (ret < 0) {
>> +               dev_err(&adev->dev,
>> +                       "probe %pOF failed, no partition id.\n", nc);
>> +               return ERR_PTR(ret);
>> +       }
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret)
>> +               return ERR_PTR(ret);
>> +
>> +       apart = aie_get_partition_from_id(adev, partition_id);
>> +       mutex_unlock(&adev->mlock);
>> +       if (apart) {
>> +               dev_err(&adev->dev,
>> +                       "probe failed: partition %u exists.\n",
>> +                       partition_id);
>> +               return ERR_PTR(ret);
>> +       }
>> +
>> +       apart = aie_create_partition(adev, &range);
>> +       if (IS_ERR(apart)) {
>> +               dev_err(&adev->dev,
>> +                       "%s: failed to create part(%u,%u),(%u,%u).\n",
>> +                       __func__, range.start.col, range.start.row,
>> +                       range.size.col, range.size.row);
>> +               return apart;
>> +       }
>> +
>> +       of_node_get(nc);
>> +       apart->dev.of_node = nc;
>> +       apart->partition_id = partition_id;
>> +
>> +       dev_info(&adev->dev,
>> +                "AI engine part(%u,%u),(%u,%u), id %u is probed
>> successfully.\n",
>> +                range.start.col, range.start.row,
>> +                range.size.col, range.size.row, apart->partition_id);
>> +
>> +       return apart;
>> +}
>> +
>> +/**
>> + * aie_destroy_part() - destroy AI engine partition
>> + * @apart: AI engine partition
>> + *
>> + * This function will remove AI engine partition.
>> + */
>> +void aie_part_remove(struct aie_partition *apart)
>> +{
>> +       device_del(&apart->dev);
>> +       put_device(&apart->dev);
>> +}
>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-res.c
>> b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
>> new file mode 100644
>> index 0000000..36f08bf
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
>> @@ -0,0 +1,114 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Xilinx AI Engine device driver
>> + *
>> + * Copyright (C) 2020 Xilinx, Inc.
>> + */
>> +
>> +#include <linux/bitmap.h>
>> +
>> +#include "ai-engine-internal.h"
>> +
>> +/**
>> + * aie_resource_initialize() - initialize AI engine resource
>> + * @res: pointer to AI engine resource
>> + * @count: total number of element of this resource
>> + * @return: 0 for success, negative value for failure.
>> + *
>> + * This function will initialize the data structure for the
>> + * resource.
>> + */
>> +int aie_resource_initialize(struct aie_resource *res, int count)
>> +{
>> +       if (!res || !count)
>> +               return -EINVAL;
>> +       res->bitmap = bitmap_zalloc(count, GFP_KERNEL);
>> +       if (!res->bitmap)
>> +               return -ENOMEM;
>> +       res->total = count;
>> +
>> +       return 0;
>> +}
>> +
>> +/**
>> + * aie_resource_uninitialize() - uninitialize AI engine resource
>> + * @res: pointer to AI engine resource
>> + *
>> + * This function will release the AI engine resource data members.
>> + */
>> +void aie_resource_uninitialize(struct aie_resource *res)
>> +{
>> +       res->total = 0;
>> +       if (res->bitmap)
>> +               bitmap_free(res->bitmap);
>> +}
>> +
>> +/**
>> + * aie_resource_check() - check availability of requested resource
>> + * @res: pointer to AI engine resource to check
>> + * @start: start index of the required resource, it will only be used if
>> + *        @continuous is 1. It will check the available resource starting
>> from
>> + *        @start
>> + * @count: number of requested element
>> + * @return: start resource id if the requested number of resources are
>> available
>> + *         It will return negative value of errors.
>> + *
>> + * This function will check the availability. It will return start resource
>> id
>> + * if the requested number of resources are available.
>> + */
>> +int aie_resource_check_region(struct aie_resource *res,
>> +                             u32 start, u32 count)
>> +{
>> +       unsigned long id;
>> +
>> +       if (!res || !res->bitmap || !count)
>> +               return -EINVAL;
>> +       id = bitmap_find_next_zero_area(res->bitmap, res->total, start,
>> +                                       count, 0);
>> +       if (id >= res->total)
>> +               return -ERANGE;
>> +
>> +       return (int)id;
>> +}
>> +
>> +/**
>> + * aie_resource_get_region() - get requested AI engine resource
>> + * @res: pointer to AI engine resource to check
>> + * @count: number of requested element
>> + * @start: start index of the required resource
>> + * @return: start resource id for success, and negative value for failure.
>> + *
>> + * This function check if the requested AI engine resource is available.
>> + * If it is available, mark it used and return the start resource id.
>> + */
>> +int aie_resource_get_region(struct aie_resource *res, u32 start, u32 count)
>> +{
>> +       unsigned long off;
>> +
>> +       if (!res || !res->bitmap || !count)
>> +               return -EINVAL;
>> +       off = bitmap_find_next_zero_area(res->bitmap, res->total, start,
>> +                                        count, 0);
>> +       if (off >= res->total) {
>> +               pr_err("Failed to get available AI engine resource.\n");
>> +               return -ERANGE;
>> +       }
>> +       bitmap_set(res->bitmap, off, count);
>> +
>> +       return (int)off;
>> +}
>> +
>> +/**
>> + * aie_resource_put_region() - release requested AI engine resource
>> + * @res: pointer to AI engine resource to check
>> + * @start: start index of the resource to release
>> + * @count: number of elements to release
>> + *
>> + * This function release the requested AI engine resource.
>> + */
>> +void aie_resource_put_region(struct aie_resource *res, int start, u32 count)
>> +{
>> +       if (!res || !count)
>> +               return;
>> +       bitmap_clear(res->bitmap, start, count);
>> +}
>> diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-
>> engine.h
>> new file mode 100644
>> index 0000000..acbc781
>> --- /dev/null
>> +++ b/include/uapi/linux/xlnx-ai-engine.h
>> @@ -0,0 +1,107 @@
>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>> +/*
>> + * Copyright (c) 2020, Xilinx Inc.
>> + */
>> +
>> +#ifndef _UAPI_AI_ENGINE_H_
>> +#define _UAPI_AI_ENGINE_H_
>> +
>> +#include <linux/ioctl.h>
>> +#include <linux/types.h>
>> +
>> +enum aie_reg_op {
>> +       AIE_REG_WRITE,
>> +};
>> +
>> +/* AI engine partition is in use */
>> +#define XAIE_PART_STATUS_INUSE         (1U << 0)
>> +
>> +/**
>> + * struct aie_location - AIE location information
>> + * @col: column id
>> + * @row: row id
>> + */
>> +struct aie_location {
>> +       __u32 col;
>> +       __u32 row;
>> +};
>> +
>> +/**
>> + * struct aie_range - AIE range information
>> + * @start: start tile location
>> + * @size: size of the range, number of columns and rows
>> + */
>> +struct aie_range {
>> +       struct aie_location start;
>> +       struct aie_location size;
>> +};
>> +
>> +/**
>> + * struct aie_reg_args - AIE access register arguments
>> + * @op: if this request is to read, write or poll register
>> + * @mask: mask for mask write, 0 for not mask write
>> + * @offset: offset of register to the start of an AI engine partition
>> + * @val: value to write or get
>> + */
>> +struct aie_reg_args {
>> +       enum aie_reg_op op;
>> +       __u32 mask;
>> +       __u64 offset;
>> +       __u32 val;
>> +};
>> +
>> +/**
>> + * struct aie_range_args - AIE range request arguments
>> + * @partition_id: partition id. It is used to identify the
>> + *               AI engine partition in the system.
>> + * @uid: image identifier loaded on the AI engine partition
>> + * @range: range of AIE tiles
>> + * @status: indicate if the AI engine is in use.
>> + *         0 means not in used, otherwise, in use.
>> + */
>> +struct aie_range_args {
>> +       __u32 partition_id;
>> +       __u32 uid;
>> +       struct aie_range range;
>> +       __u32 status;
>> +};
>> +
>> +/**
>> + * struct aie_partition_query - AIE partition query arguments
>> + * @partition_cnt: number of defined partitions in the system
>> + * @partitions: buffer to store defined partitions information.
>> + */
>> +struct aie_partition_query {
>> +       struct aie_range_args *partitions;
>> +       __u32 partition_cnt;
>> +};
>> +
>> +/**
>> + * struct aie_partition_req - AIE request partition arguments
>> + * @partition_id: partition node id. It is used to identify the AI engine
>> + *               partition in the system.
>> + * @uid: image identifier loaded on the AI engine partition
>> + * @meta_data: meta data to indicate which resources used by application.
>> + * @flag: used for application to indicate particular driver requirements
>> + *       application wants to have for the partition. e.g. do not clean
>> + *       resource when closing the partition.
>> + */
>> +struct aie_partition_req {
>> +       __u32 partition_id;
>> +       __u32 uid;
>> +       __u64 meta_data;
>> +       __u32 flag;
>> +};
>> +
>> +#define AIE_IOCTL_BASE 'A'
>> +
>> +/* AI engine device IOCTL operations */
>> +#define AIE_ENQUIRE_PART_IOCTL         _IOWR(AIE_IOCTL_BASE, 0x1, \
>> +                                             struct aie_partition_query)
>> +#define AIE_REQUEST_PART_IOCTL         _IOR(AIE_IOCTL_BASE, 0x2, \
>> +                                            struct aie_partition_req)
>> +
>> +/* AI engine partition IOCTL operations */
>> +#define AIE_REG_IOCTL                  _IOWR(AIE_IOCTL_BASE, 0x8, \
>> +                                             struct aie_reg_args)
>> +#endif
>

2020-12-08 21:00:11

by Wendy Liang

[permalink] [raw]
Subject: Re: [PATCH 2/9] misc: Add Xilinx AI engine device driver


On 12/8/20 9:12 AM, Nicolas Dufresne wrote:
> Le mercredi 18 novembre 2020 à 00:06 -0800, Wendy Liang a écrit :
>> Create AI engine device/partition hierarchical structure.
>>
>> Each AI engine device can have multiple logical partitions(groups of AI
>> engine tiles). Each partition is column based and has its own node ID
>> in the system. AI engine device driver manages its partitions.
>>
>> Applications can access AI engine partition through the AI engine
>> partition driver instance. AI engine registers write is moved to kernel
>> as there are registers in the AI engine array needs privilege
>> permission.
> Hi there, it's nice to see an effort to upstream an AI driver. I'm a little
> worried this driver is not obvious to use from it's source code itself. So you
> have reference to some Open Source code that demonstrate it's usage ?

We have AI engine library which provides a cross platforms APIs for other

libraries/application to use the hardware. Here is the source code:

https://github.com/Xilinx/embeddedsw/tree/master/XilinxProcessorIPLib/drivers/aienginev2/src

The cross platforms AI engine library runs in LInux userspace it defines
how to

configure, and the kernel driver controls if what can be access and
manage errors from device.


Best Regards,

Wendy


>
>> Signed-off-by: Wendy Liang <[email protected]>
>> Signed-off-by: Hyun Kwon <[email protected]>
>> ---
>>  MAINTAINERS                                        |   8 +
>>  drivers/misc/Kconfig                               |  12 +
>>  drivers/misc/Makefile                              |   1 +
>>  drivers/misc/xilinx-ai-engine/Makefile             |  11 +
>>  drivers/misc/xilinx-ai-engine/ai-engine-aie.c      | 115 +++++
>>  drivers/misc/xilinx-ai-engine/ai-engine-dev.c      | 448 ++++++++++++++++++
>>  drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 226 ++++++++++
>>  drivers/misc/xilinx-ai-engine/ai-engine-part.c     | 498
>> +++++++++++++++++++++
>>  drivers/misc/xilinx-ai-engine/ai-engine-res.c      | 114 +++++
>>  include/uapi/linux/xlnx-ai-engine.h                | 107 +++++
>>  10 files changed, 1540 insertions(+)
>>  create mode 100644 drivers/misc/xilinx-ai-engine/Makefile
>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-part.c
>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-res.c
>>  create mode 100644 include/uapi/linux/xlnx-ai-engine.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 5cc595a..40e3351 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -19283,6 +19283,14 @@ T:     git https://github.com/Xilinx/linux-xlnx.git
>>  F:     Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml
>>  F:     drivers/phy/xilinx/phy-zynqmp.c
>>
>> +XILINX AI ENGINE DRIVER
>> +M:     Wendy Liang <[email protected]>
>> +S:     Supported
>> +F:     Documentation/devicetree/bindings/soc/xilinx/xlnx,ai-engine.yaml
>> +F:     drivers/misc/xilinx-ai-engine/
>> +F:     include/linux/xlnx-ai-engine.h
>> +F:     include/uapi/linux/xlnx-ai-engine.h
>> +
>>  XILLYBUS DRIVER
>>  M:     Eli Billauer <[email protected]>
>>  L:     [email protected]
>> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
>> index fafa8b0..0b8ce4d 100644
>> --- a/drivers/misc/Kconfig
>> +++ b/drivers/misc/Kconfig
>> @@ -444,6 +444,18 @@ config XILINX_SDFEC
>>
>>           If unsure, say N.
>>
>> +config XILINX_AIE
>> +       tristate "Xilinx AI engine"
>> +       depends on ARM64 || COMPILE_TEST
>> +       help
>> +         This option enables support for the Xilinx AI engine driver.
>> +         One Xilinx AI engine device can have multiple partitions (groups of
>> +         AI engine tiles). Xilinx AI engine device driver instance manages
>> +         AI engine partitions. User application access its partitions through
>> +         AI engine partition instance file operations.
>> +
>> +         If unsure, say N
>> +
>>  config MISC_RTSX
>>         tristate
>>         default MISC_RTSX_PCI || MISC_RTSX_USB
>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>> index d23231e..2176b18 100644
>> --- a/drivers/misc/Makefile
>> +++ b/drivers/misc/Makefile
>> @@ -57,3 +57,4 @@ obj-$(CONFIG_HABANA_AI)               += habanalabs/
>>  obj-$(CONFIG_UACCE)            += uacce/
>>  obj-$(CONFIG_XILINX_SDFEC)     += xilinx_sdfec.o
>>  obj-$(CONFIG_HISI_HIKEY_USB)   += hisi_hikey_usb.o
>> +obj-$(CONFIG_XILINX_AIE)       += xilinx-ai-engine/
>> diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-
>> engine/Makefile
>> new file mode 100644
>> index 0000000..7827a0a
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/Makefile
>> @@ -0,0 +1,11 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +#
>> +# Makefile for Xilinx AI engine device driver
>> +#
>> +
>> +obj-$(CONFIG_XILINX_AIE)       += xilinx-aie.o
>> +
>> +xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \
>> +                                  ai-engine-dev.o \
>> +                                  ai-engine-part.o \
>> +                                  ai-engine-res.o
>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>> b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>> new file mode 100644
>> index 0000000..319260f
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>> @@ -0,0 +1,115 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Xilinx AI Engine driver AIE device specific implementation
>> + *
>> + * Copyright (C) 2020 Xilinx, Inc.
>> + */
>> +
>> +#include <linux/slab.h>
>> +
>> +#include "ai-engine-internal.h"
>> +
>> +#define AIE_ARRAY_SHIFT                30U
>> +#define AIE_COL_SHIFT          23U
>> +#define AIE_ROW_SHIFT          18U
>> +
>> +/*
>> + * Registers offsets
>> + */
>> +#define AIE_SHIMNOC_L2INTR_MASK_REGOFF         0x00015000U
>> +#define AIE_SHIMNOC_L2INTR_INTR_REGOFF         0x00015010U
>> +#define AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF     0x0001d000U
>> +#define AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF     0x0001d13cU
>> +#define AIE_SHIMNOC_AXIMM_REGOFF               0x0001e020U
>> +#define AIE_SHIMPL_L1INTR_MASK_A_REGOFF                0x00035000U
>> +#define AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF 0x00035050U
>> +#define AIE_SHIMPL_CLKCNTR_REGOFF              0x00036040U
>> +#define AIE_SHIMPL_RESET_REGOFF                        0x0003604cU
>> +#define AIE_TILE_CORE_CLKCNTR_REGOFF           0x00036040U
>> +
>> +static const struct aie_tile_regs aie_kernel_regs[] = {
>> +       /* SHIM AXI MM Config */
>> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMNOC_AXIMM_REGOFF,
>> +        .eoff = AIE_SHIMNOC_AXIMM_REGOFF,
>> +       },
>> +       /* SHIM DMA ADDRESS range */
>> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF,
>> +        .eoff = AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF,
>> +       },
>> +       /* SHIM 2nd level interrupt controller */
>> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMNOC_L2INTR_MASK_REGOFF,
>> +        .eoff = AIE_SHIMNOC_L2INTR_INTR_REGOFF,
>> +       },
>> +       /* SHIM 1st level interrupt controller */
>> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
>> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMPL_L1INTR_MASK_A_REGOFF,
>> +        .eoff = AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF,
>> +       },
>> +       /* SHIM reset Enable */
>> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
>> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMPL_RESET_REGOFF,
>> +        .eoff = AIE_SHIMPL_RESET_REGOFF,
>> +       },
>> +       /* SHIM clock control */
>> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
>> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_SHIMPL_CLKCNTR_REGOFF,
>> +        .eoff = AIE_SHIMPL_CLKCNTR_REGOFF,
>> +       },
>> +       /* Tile clock control */
>> +       {.attribute = AIE_TILE_TYPE_TILE << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>> +        .soff = AIE_TILE_CORE_CLKCNTR_REGOFF,
>> +        .eoff = AIE_TILE_CORE_CLKCNTR_REGOFF,
>> +       },
>> +};
>> +
>> +static u32 aie_get_tile_type(struct aie_location *loc)
>> +{
>> +       if (loc->row)
>> +               return AIE_TILE_TYPE_TILE;
>> +       /* SHIM row */
>> +       if ((loc->col % 4) < 2)
>> +               return AIE_TILE_TYPE_SHIMPL;
>> +
>> +       return AIE_TILE_TYPE_SHIMNOC;
>> +}
>> +
>> +static const struct aie_tile_operations aie_ops = {
>> +       .get_tile_type = aie_get_tile_type,
>> +};
>> +
>> +/**
>> + * aie_device_init() - Initialize AI engine device struct AIE specific
>> + *                    properties
>> + * @adev: AI engine device
>> + * @return: 0 for success, negative value for failure.
>> + *
>> + * This function initialize the AI engine device structure device version
>> + * specific elements such as register addressing related array shift,
>> + * column shift, and row shift; AIE specific device operations, device
>> + * columns resource.
>> + */
>> +int aie_device_init(struct aie_device *adev)
>> +{
>> +       int ret;
>> +
>> +       adev->array_shift = AIE_ARRAY_SHIFT;
>> +       adev->col_shift = AIE_COL_SHIFT;
>> +       adev->row_shift = AIE_ROW_SHIFT;
>> +       adev->ops = &aie_ops;
>> +       adev->num_kernel_regs = ARRAY_SIZE(aie_kernel_regs);
>> +       adev->kernel_regs = aie_kernel_regs;
>> +
>> +       /* Get the columns resource */
>> +       /* Get number of columns from AI engine memory resource */
>> +       ret = aie_resource_initialize(&adev->cols_res, 50);
>> +       if (ret)
>> +               dev_err(&adev->dev, "failed to initialize columns
>> resource.\n");
>> +
>> +       return ret;
>> +}
>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>> b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>> new file mode 100644
>> index 0000000..2ab2dc8
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>> @@ -0,0 +1,448 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Xilinx AI Engine device driver
>> + *
>> + * Copyright (C) 2020 Xilinx, Inc.
>> + */
>> +
>> +#include <linux/anon_inodes.h>
>> +#include <linux/cdev.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/file.h>
>> +#include <linux/fs.h>
>> +#include <linux/idr.h>
>> +#include <linux/list.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/uaccess.h>
>> +#include <uapi/linux/xlnx-ai-engine.h>
>> +
>> +#include "ai-engine-internal.h"
>> +
>> +#define AIE_DEV_MAX    (MINORMASK + 1)
>> +
>> +static dev_t aie_major;
>> +struct class *aie_class;
>> +
>> +static DEFINE_IDA(aie_device_ida);
>> +static DEFINE_IDA(aie_minor_ida);
>> +
>> +/**
>> + * aie_get_partition_fd() - Get AI engine partition file descriptor
>> + * @apart: AI engine partition
>> + * @return: file descriptor for AI engine partition for success, or negative
>> + *         value for failure.
>> + *
>> + * This function gets a file descriptor for the AI engine partition.
>> + */
>> +static int aie_get_partition_fd(struct aie_partition *apart)
>> +{
>> +       struct file *filep;
>> +       int ret;
>> +
>> +       /*
>> +        * We can't use anon_inode_getfd() because we need to modify
>> +        * the f_mode flags directly to allow more than just ioctls
>> +        */
>> +       ret = get_unused_fd_flags(O_CLOEXEC);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       filep = anon_inode_getfile(dev_name(&apart->dev), &aie_part_fops,
>> +                                  apart, O_RDWR);
>> +       if (IS_ERR(filep)) {
>> +               put_unused_fd(ret);
>> +               ret = PTR_ERR(filep);
>> +               return ret;
>> +       }
>> +       filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
>> +       fd_install(ret, filep);
>> +
>> +       return ret;
>> +}
>> +
>> +/**
>> + * aie_enquire_partitions() - get AI engine partitions information
>> + * @adev: AI engine device
>> + * @query: data struct to store the partition information
>> + * @return: 0 for success, and negative value for failure.
>> + */
>> +static int aie_enquire_partitions(struct aie_device *adev,
>> +                                 struct aie_partition_query *query)
>> +{
>> +       struct aie_partition *apart;
>> +       u32 partition_cnt, i = 0;
>> +       int ret;
>> +
>> +       if (!query->partitions) {
>> +               /*
>> +                * If partitions information buffer is NULL.
>> +                * It is to get the number of partitions.
>> +                */
>> +               query->partition_cnt = 0;
>> +               list_for_each_entry(apart, &adev->partitions, node)
>> +                       query->partition_cnt++;
>> +               return 0;
>> +       }
>> +
>> +       partition_cnt = query->partition_cnt;
>> +       if (!partition_cnt)
>> +               return 0;
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret)
>> +               return ret;
>> +
>> +       list_for_each_entry(apart, &adev->partitions, node) {
>> +               struct aie_range_args part;
>> +
>> +               if (i >= partition_cnt)
>> +                       break;
>> +               part.partition_id = apart->partition_id;
>> +               /*
>> +                * TBD: check with PLM that if the partition is programmed
>> +                * and get the UID of the image which is loaded on the AI
>> +                * engine partition.
>> +                */
>> +               part.uid = 0;
>> +               part.range.start.col = apart->range.start.col;
>> +               part.range.start.row = apart->range.start.row;
>> +               part.range.size.col = apart->range.size.col;
>> +               part.range.size.row = apart->range.size.row;
>> +               /* Check if partition is in use */
>> +               part.status = apart->status;
>> +               if (copy_to_user((void __user *)&query->partitions[i], &part,
>> +                                sizeof(part))) {
>> +                       mutex_unlock(&adev->mlock);
>> +                       return -EFAULT;
>> +               }
>> +               i++;
>> +       }
>> +       mutex_unlock(&adev->mlock);
>> +       query->partition_cnt = i;
>> +
>> +       return 0;
>> +}
>> +
>> +/**
>> + * aie_get_partition_from_id() - get AI engine partition from id
>> + * @adev: AI engine device
>> + * @partition_id: partition id to check
>> + * @return: partition pointer if partition exists, otherwise, NULL.
>> + *
>> + * This function checks defined partitions with partition id.
>> + * This function expect the caller to lock mlock of @adev.
>> + */
>> +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
>> +                                               u32 partition_id)
>> +{
>> +       struct aie_partition *apart;
>> +
>> +       list_for_each_entry(apart, &adev->partitions, node) {
>> +               if (apart->partition_id == partition_id)
>> +                       return apart;
>> +       }
>> +
>> +       return NULL;
>> +}
>> +
>> +/**
>> + * aie_request_partition() - request AI engine partition
>> + * @adev: AI engine device
>> + * @req: partition request, includes the requested AI engine information
>> + *      such as partition node ID and the UID of the image which is
>> + *      loaded on the partition.
>> + * @return: partition pointer if partition exists, otherwise, NULL.
>> + *
>> + * This function finds a defined partition which matches the specified
>> + * partition id, request it if it hasn't been requested, and returns it.
>> + */
>> +struct aie_partition *aie_request_partition(struct aie_device *adev,
>> +                                           struct aie_partition_req *req)
>> +{
>> +       struct aie_partition *apart;
>> +       int ret;
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret)
>> +               return ERR_PTR(ret);
>> +
>> +       apart = aie_get_partition_from_id(adev, req->partition_id);
>> +       if (!apart) {
>> +               dev_err(&adev->dev,
>> +                       "request partition %u failed, not exist.\n",
>> +                       req->partition_id);
>> +               mutex_unlock(&adev->mlock);
>> +               return ERR_PTR(-EINVAL);
>> +       }
>> +       /*
>> +        * TBD: It will check image UID too to see if the user matches
>> +        * what's loaded in the AI engine partition. And check the meta
>> +        * data to see which resources used by application.
>> +        */
>> +
>> +       ret = mutex_lock_interruptible(&apart->mlock);
>> +       if (ret)
>> +               return ERR_PTR(ret);
>> +
>> +       if (apart->status & XAIE_PART_STATUS_INUSE) {
>> +               mutex_unlock(&apart->mlock);
>> +               dev_err(&adev->dev,
>> +                       "request partition %u failed, partition in use.\n",
>> +                       req->partition_id);
>> +               apart = ERR_PTR(-EBUSY);
>> +       } else {
>> +               /*
>> +                * TBD:
>> +                * 1. setup NOC AXI MM config to only generate error events
>> +                *    for slave error and decode error.
>> +                * 2. scan to see which tiles have been clock gated.
>> +                *
>> +                * This needs to be done before the AI engine partition is
>> +                * exported for user to access.
>> +                */
>> +               apart->status = XAIE_PART_STATUS_INUSE;
>> +               mutex_unlock(&apart->mlock);
>> +       }
>> +       mutex_unlock(&adev->mlock);
>> +
>> +       return apart;
>> +}
>> +
>> +static long xilinx_ai_engine_ioctl(struct file *filp, unsigned int cmd,
>> +                                  unsigned long arg)
>> +{
>> +       struct inode *inode = file_inode(filp);
>> +       struct aie_device *adev = cdev_to_aiedev(inode->i_cdev);
>> +       void __user *argp = (void __user *)arg;
>> +       int ret;
>> +
>> +       switch (cmd) {
>> +       case AIE_ENQUIRE_PART_IOCTL:
>> +       {
>> +               struct aie_partition_query query;
>> +               struct aie_partition_query  __user *uquery_ptr = argp;
>> +
>> +               if (copy_from_user(&query, uquery_ptr, sizeof(query)))
>> +                       return -EFAULT;
>> +               ret = aie_enquire_partitions(adev, &query);
>> +               if (ret < 0)
>> +                       return ret;
>> +               if (copy_to_user((void __user *)&uquery_ptr->partition_cnt,
>> +                                &query.partition_cnt,
>> +                                sizeof(query.partition_cnt)))
>> +                       return -EFAULT;
>> +               break;
>> +       }
>> +       case AIE_REQUEST_PART_IOCTL:
>> +       {
>> +               struct aie_partition_req req;
>> +               struct aie_partition *apart;
>> +
>> +               if (copy_from_user(&req, argp, sizeof(req)))
>> +                       return -EFAULT;
>> +               apart = aie_request_partition(adev, &req);
>> +               if (IS_ERR(apart))
>> +                       return PTR_ERR(apart);
>> +               ret = aie_get_partition_fd(apart);
>> +               if (ret < 0) {
>> +                       dev_err(&apart->dev, "failed to get fd.\n");
>> +                       break;
>> +               }
>> +               break;
>> +       }
>> +       default:
>> +               dev_err(&adev->dev, "Invalid ioctl command %u.\n", cmd);
>> +               ret = -EINVAL;
>> +               break;
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +static const struct file_operations aie_device_fops = {
>> +       .owner          = THIS_MODULE,
>> +       .unlocked_ioctl = xilinx_ai_engine_ioctl,
>> +};
>> +
>> +static void xilinx_ai_engine_release_device(struct device *dev)
>> +{
>> +       struct aie_device *adev = dev_to_aiedev(dev);
>> +
>> +       ida_simple_remove(&aie_device_ida, dev->id);
>> +       ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
>> +       cdev_del(&adev->cdev);
>> +       aie_resource_uninitialize(&adev->cols_res);
>> +}
>> +
>> +/**
>> + * of_xilinx_ai_engine_part_probe() - probes for AI engine partition nodes
>> + * @adev: AI engine device
>> + *
>> + * This function will probe for children AI engine partition nodes and create
>> + * an AI engine partition instance for each node.
>> + */
>> +static void of_xilinx_ai_engine_part_probe(struct aie_device *adev)
>> +{
>> +       struct device_node *nc;
>> +
>> +       for_each_available_child_of_node(adev->dev.of_node, nc) {
>> +               struct aie_partition *apart;
>> +
>> +               if (of_node_test_and_set_flag(nc, OF_POPULATED))
>> +                       continue;
>> +               apart = of_aie_part_probe(adev, nc);
>> +               if (IS_ERR(apart)) {
>> +                       dev_err(&adev->dev,
>> +                               "Failed to probe AI engine part for %pOF\n",
>> +                               nc);
>> +                       of_node_clear_flag(nc, OF_POPULATED);
>> +               }
>> +       }
>> +}
>> +
>> +static int xilinx_ai_engine_probe(struct platform_device *pdev)
>> +{
>> +       struct aie_device *adev;
>> +       struct device *dev;
>> +       int ret;
>> +
>> +       adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
>> +       if (!adev)
>> +               return -ENOMEM;
>> +       platform_set_drvdata(pdev, adev);
>> +       INIT_LIST_HEAD(&adev->partitions);
>> +       mutex_init(&adev->mlock);
>> +
>> +       adev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       if (!adev->res) {
>> +               dev_err(&pdev->dev, "No memory resource.\n");
>> +               return -EINVAL;
>> +       }
>> +       adev->base = devm_ioremap_resource(&pdev->dev, adev->res);
>> +       if (IS_ERR(adev->base)) {
>> +               dev_err(&pdev->dev, "no io memory resource.\n");
>> +               return PTR_ERR(adev->base);
>> +       }
>> +
>> +       /* Initialize AIE device specific instance. */
>> +       ret = aie_device_init(adev);
>> +       if (ret < 0) {
>> +               dev_err(&pdev->dev, "failed to initialize device
>> instance.\n");
>> +               return ret;
>> +       }
>> +
>> +       dev = &adev->dev;
>> +       device_initialize(dev);
>> +       dev->class = aie_class;
>> +       dev->parent = &pdev->dev;
>> +       dev->of_node = pdev->dev.of_node;
>> +
>> +       ret = ida_simple_get(&aie_minor_ida, 0, AIE_DEV_MAX, GFP_KERNEL);
>> +       if (ret < 0)
>> +               goto free_dev;
>> +       dev->devt = MKDEV(MAJOR(aie_major), ret);
>> +       ret = ida_simple_get(&aie_device_ida, 0, 0, GFP_KERNEL);
>> +       if (ret < 0)
>> +               goto free_minor_ida;
>> +       dev->id = ret;
>> +       dev_set_name(&adev->dev, "aie%d", dev->id);
>> +
>> +       cdev_init(&adev->cdev, &aie_device_fops);
>> +       adev->cdev.owner = THIS_MODULE;
>> +       ret = cdev_add(&adev->cdev, dev->devt, 1);
>> +       if (ret)
>> +               goto free_ida;
>> +       /* We can now rely on the release function for cleanup */
>> +       dev->release = xilinx_ai_engine_release_device;
>> +
>> +       ret = device_add(dev);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "device_add failed: %d\n", ret);
>> +               put_device(dev);
>> +               return ret;
>> +       }
>> +
>> +       of_xilinx_ai_engine_part_probe(adev);
>> +       dev_info(&pdev->dev, "Xilinx AI Engine device(cols=%u) probed\n",
>> +                adev->cols_res.total);
>> +       return 0;
>> +
>> +free_ida:
>> +       ida_simple_remove(&aie_device_ida, dev->id);
>> +free_minor_ida:
>> +       ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
>> +free_dev:
>> +       put_device(dev);
>> +
>> +       return ret;
>> +}
>> +
>> +static int xilinx_ai_engine_remove(struct platform_device *pdev)
>> +{
>> +       struct aie_device *adev = platform_get_drvdata(pdev);
>> +       struct aie_partition *apart;
>> +
>> +       list_for_each_entry(apart, &adev->partitions, node)
>> +               aie_part_remove(apart);
>> +
>> +       device_del(&adev->dev);
>> +       put_device(&adev->dev);
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id xilinx_ai_engine_of_match[] = {
>> +       { .compatible = "xlnx,ai-engine-v1.0", },
>> +       { /* end of table */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, xilinx_ai_engine_of_match);
>> +
>> +static struct platform_driver xilinx_ai_engine_driver = {
>> +       .probe                  = xilinx_ai_engine_probe,
>> +       .remove                 = xilinx_ai_engine_remove,
>> +       .driver                 = {
>> +               .name           = "xilinx-ai-engine",
>> +               .of_match_table = xilinx_ai_engine_of_match,
>> +       },
>> +};
>> +
>> +static int __init xilinx_ai_engine_init(void)
>> +{
>> +       int ret;
>> +
>> +       ret = alloc_chrdev_region(&aie_major, 0, AIE_DEV_MAX, "aie");
>> +       if (ret < 0) {
>> +               pr_err("aie: failed to allocate aie region\n");
>> +               return ret;
>> +       }
>> +
>> +       aie_class = class_create(THIS_MODULE, "aie");
>> +       if (IS_ERR(aie_class)) {
>> +               pr_err("failed to create aie class\n");
>> +               unregister_chrdev_region(aie_major, AIE_DEV_MAX);
>> +               return PTR_ERR(aie_class);
>> +       }
>> +
>> +       platform_driver_register(&xilinx_ai_engine_driver);
>> +
>> +       return 0;
>> +}
>> +postcore_initcall(xilinx_ai_engine_init);
>> +
>> +static void __exit xilinx_ai_engine_exit(void)
>> +{
>> +       platform_driver_unregister(&xilinx_ai_engine_driver);
>> +       class_destroy(aie_class);
>> +       unregister_chrdev_region(aie_major, AIE_DEV_MAX);
>> +}
>> +module_exit(xilinx_ai_engine_exit);
>> +
>> +MODULE_AUTHOR("Xilinx, Inc.");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>> b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>> new file mode 100644
>> index 0000000..6a69946
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>> @@ -0,0 +1,226 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Xilinx AI Engine driver internal header
>> + *
>> + * Copyright (C) 2020 Xilinx, Inc.
>> + */
>> +
>> +#ifndef AIE_INTERNAL_H
>> +#define AIE_INTERNAL_H
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/bits.h>
>> +#include <linux/cdev.h>
>> +#include <linux/device.h>
>> +#include <linux/io.h>
>> +#include <linux/list.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <uapi/linux/xlnx-ai-engine.h>
>> +
>> +/*
>> + * Macros for AI engine tile type bitmasks
>> + */
>> +#define AIE_TILE_TYPE_TILE     BIT(0)
>> +#define AIE_TILE_TYPE_SHIMPL   BIT(1)
>> +/* SHIM NOC tile includes SHIM PL and SHIM NOC modules */
>> +#define AIE_TILE_TYPE_SHIMNOC  BIT(2)
>> +
>> +/*
>> + * Macros for attribute property of AI engine registers accessed by kernel
>> + * 0 - 7 bits: tile type bits
>> + * 8 - 15 bits: permission bits. If it is 1, it allows write from userspace
>> + */
>> +#define AIE_REGS_ATTR_TILE_TYPE_SHIFT  0U
>> +#define AIE_REGS_ATTR_PERM_SHIFT       8U
>> +#define AIE_REGS_ATTR_TILE_TYPE_MASK   GENMASK(AIE_REGS_ATTR_PERM_SHIFT - 1,
>> \
>> +                                               AIE_REGS_ATTR_TILE_TYPE_SHIFT)
>> +#define AIE_REGS_ATTR_PERM_MASK                GENMASK(15, \
>> +                                               AIE_REGS_ATTR_PERM_SHIFT)
>> +
>> +/**
>> + * struct aie_tile_regs - contiguous range of AI engine register
>> + *                       within an AI engine tile
>> + * @soff: start offset of the range
>> + * @eoff: end offset of the range
>> + * @attribute: registers attribute. It uses AIE_REGS_ATTR_* macros defined
>> + *            above.
>> + */
>> +struct aie_tile_regs {
>> +       size_t soff;
>> +       size_t eoff;
>> +       u32 attribute;
>> +};
>> +
>> +struct aie_device;
>> +struct aie_partition;
>> +
>> +/**
>> + * struct aie_tile_operations - AI engine device operations
>> + * @get_tile_type: get type of tile based on tile operation
>> + *
>> + * Different AI engine device version has its own device
>> + * operation.
>> + */
>> +struct aie_tile_operations {
>> +       u32 (*get_tile_type)(struct aie_location *loc);
>> +};
>> +
>> +/**
>> + * struct aie_resource - AI engine resource structure
>> + * @bitmap: resource bitmap
>> + * @total: total number of resource
>> + */
>> +struct aie_resource {
>> +       unsigned long *bitmap;
>> +       u32 total;
>> +};
>> +
>> +/**
>> + * struct aie_device - AI engine device structure
>> + * @partitions: list of partitions requested
>> + * @cdev: cdev for the AI engine
>> + * @dev: device for the AI engine device
>> + * @mlock: protection for AI engine device operations
>> + * @base: AI engine device base virtual address
>> + * @res: memory resource of AI engine device
>> + * @kernel_regs: array of kernel only registers
>> + * @ops: tile operations
>> + * @size: size of the AI engine address space
>> + * @array_shift: array address shift
>> + * @col_shift: column address shift
>> + * @row_shift: row address shift
>> + * @cols_res: AI engine columns resources to indicate
>> + *           while columns are occupied by partitions.
>> + * @num_kernel_regs: number of kernel only registers range
>> + * @version: AI engine device version
>> + */
>> +struct aie_device {
>> +       struct list_head partitions;
>> +       struct cdev cdev;
>> +       struct device dev;
>> +       struct mutex mlock; /* protection for AI engine partitions */
>> +       void __iomem *base;
>> +       struct resource *res;
>> +       const struct aie_tile_regs *kernel_regs;
>> +       const struct aie_tile_operations *ops;
>> +       size_t size;
>> +       struct aie_resource cols_res;
>> +       u32 array_shift;
>> +       u32 col_shift;
>> +       u32 row_shift;
>> +       u32 num_kernel_regs;
>> +       int version;
>> +};
>> +
>> +/**
>> + * struct aie_partition - AI engine partition structure
>> + * @node: list node
>> + * @adev: pointer to AI device instance
>> + * @range: range of partition
>> + * @mlock: protection for AI engine partition operations
>> + * @dev: device for the AI engine partition
>> + * @partition_id: partition id. Partition ID is the identifier
>> + *               of the AI engine partition in the system.
>> + * @status: indicate if the partition is in use
>> + */
>> +struct aie_partition {
>> +       struct list_head node;
>> +       struct aie_device *adev;
>> +       struct aie_range range;
>> +       struct mutex mlock; /* protection for AI engine partition operations
>> */
>> +       struct device dev;
>> +       u32 partition_id;
>> +       u32 status;
>> +};
>> +
>> +extern struct class *aie_class;
>> +extern const struct file_operations aie_part_fops;
>> +
>> +#define cdev_to_aiedev(i_cdev) container_of((i_cdev), struct aie_device,
>> cdev)
>> +#define dev_to_aiedev(_dev) container_of((_dev), struct aie_device, dev)
>> +#define dev_to_aiepart(_dev) container_of((_dev), struct aie_partition, dev)
>> +
>> +#define aie_col_mask(adev) ({ \
>> +       struct aie_device *_adev = (adev); \
>> +       GENMASK_ULL(_adev->array_shift - 1, _adev->col_shift);  \
>> +       })
>> +
>> +#define aie_row_mask(adev) ({ \
>> +       struct aie_device *_adev = (adev); \
>> +       GENMASK_ULL(_adev->col_shift - 1, _adev->row_shift);  \
>> +       })
>> +
>> +#define aie_tile_reg_mask(adev) ({ \
>> +       struct aie_device *_adev = (adev); \
>> +       GENMASK_ULL(_adev->row_shift - 1, 0);  \
>> +       })
>> +
>> +/*
>> + * Need to define field get, as AI engine shift mask is not constant.
>> + * Cannot use FIELD_GET()
>> + */
>> +#define aie_tile_reg_field_get(mask, shift, regoff) ( \
>> +       ((regoff) & (mask)) >> (shift))
>> +
>> +#define aie_cal_tile_reg(adev, regoff) ( \
>> +       aie_tile_reg_field_get(aie_tile_reg_mask(adev), 0, regoff))
>> +
>> +/**
>> + * aie_cal_regoff() - calculate register offset to the whole AI engine
>> + *                   device start address
>> + * @adev: AI engine device
>> + * @loc: AI engine tile location
>> + * @regoff_intile: register offset within a tile
>> + * @return: register offset to the whole AI engine device start address
>> + */
>> +static inline u32 aie_cal_regoff(struct aie_device *adev,
>> +                                struct aie_location loc, u32 regoff_intile)
>> +{
>> +       return regoff_intile + (loc.col << adev->col_shift) +
>> +              (loc.row << adev->row_shift);
>> +}
>> +
>> +/**
>> + * aie_validate_location() - validate tile location within an AI engine
>> + *                          partition
>> + * @apart: AI engine partition
>> + * @loc: AI engine tile location
>> + * @return: return 0 if it is valid, negative value for errors.
>> + *
>> + * This function checks if the AI engine location is within the AI engine
>> + * partition.
>> + */
>> +static inline int aie_validate_location(struct aie_partition *apart,
>> +                                       struct aie_location loc)
>> +{
>> +       if (loc.col < apart->range.start.col ||
>> +           loc.col >= apart->range.start.col + apart->range.size.col ||
>> +           loc.row < apart->range.start.row ||
>> +           loc.row >= apart->range.start.row + apart->range.size.row)
>> +               return -EINVAL;
>> +
>> +       return 0;
>> +}
>> +
>> +int aie_resource_initialize(struct aie_resource *res, int count);
>> +void aie_resource_uninitialize(struct aie_resource *res);
>> +int aie_resource_check_region(struct aie_resource *res, u32 start,
>> +                             u32 count);
>> +int aie_resource_get_region(struct aie_resource *res, u32 start,
>> +                           u32 count);
>> +void aie_resource_put_region(struct aie_resource *res, int start, u32 count);
>> +
>> +const struct file_operations *aie_part_get_fops(void);
>> +u8 aie_part_in_use(struct aie_partition *apart);
>> +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
>> +                                               u32 partition_id);
>> +struct aie_partition *aie_request_partition(struct aie_device *adev,
>> +                                           struct aie_partition_req *req);
>> +struct aie_partition *of_aie_part_probe(struct aie_device *adev,
>> +                                       struct device_node *nc);
>> +void aie_part_remove(struct aie_partition *apart);
>> +
>> +int aie_device_init(struct aie_device *adev);
>> +#endif /* AIE_INTERNAL_H */
>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c
>> b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
>> new file mode 100644
>> index 0000000..fc8f9f5
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
>> @@ -0,0 +1,498 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Xilinx AI Engine partition driver
>> + *
>> + * Copyright (C) 2020 Xilinx, Inc.
>> + */
>> +
>> +#include <linux/cdev.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/fs.h>
>> +#include <linux/kernel.h>
>> +#include <linux/list.h>
>> +#include <linux/mm.h>
>> +#include <linux/mman.h>
>> +#include <linux/mmu_context.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/uio.h>
>> +#include <uapi/linux/xlnx-ai-engine.h>
>> +
>> +#include "ai-engine-internal.h"
>> +
>> +/**
>> + * aie_cal_loc() - calculate tile location from register offset to the AI
>> + *                engine device
>> + * @adev: AI engine device
>> + * @loc: memory pointer to restore returning location information
>> + * @regoff: tile internal register offset
>> + *
>> + * This function returns the tile location.
>> + */
>> +static void aie_cal_loc(struct aie_device *adev,
>> +                       struct aie_location *loc, u64 regoff)
>> +{
>> +       loc->col = (u32)aie_tile_reg_field_get(aie_col_mask(adev),
>> +                                              adev->col_shift, regoff);
>> +       loc->row = (u32)aie_tile_reg_field_get(aie_row_mask(adev),
>> +                                              adev->row_shift, regoff);
>> +}
>> +
>> +/**
>> + * aie_part_reg_validation() - validate AI engine partition register access
>> + * @apart: AI engine partition
>> + * @offset: AI engine register offset
>> + * @len: len of data to write/read
>> + * @is_write: is the access to write to register
>> + * @return: 0 for success, or negative value for failure.
>> + *
>> + * This function validate if the register to access is within the AI engine
>> + * partition. If it is write access, if the register is writable by user.
>> + */
>> +static int aie_part_reg_validation(struct aie_partition *apart, size_t
>> offset,
>> +                                  size_t len, u8 is_write)
>> +{
>> +       struct aie_device *adev;
>> +       u32 regend32, ttype;
>> +       u64 regoff, regend64;
>> +       struct aie_location loc;
>> +       unsigned int i;
>> +
>> +       adev = apart->adev;
>> +       if (offset % sizeof(u32)) {
>> +               dev_err(&apart->dev,
>> +                       "Invalid reg off(0x%zx), not 32bit aligned.\n",
>> +                       offset);
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (len % sizeof(u32)) {
>> +               dev_err(&apart->dev, "Invalid reg operation len %zu.\n", len);
>> +               return -EINVAL;
>> +       }
>> +
>> +       regoff = aie_cal_tile_reg(adev, offset);
>> +       regend64 = regoff + len;
>> +       if (regend64 >= BIT_ULL(adev->row_shift)) {
>> +               dev_err(&apart->dev,
>> +                       "Invalid reg operation len %zu.\n", len);
>> +               return -EINVAL;
>> +       }
>> +
>> +       aie_cal_loc(adev, &loc, offset);
>> +       if (aie_validate_location(apart, loc)) {
>> +               dev_err(&apart->dev,
>> +                       "Invalid (%d,%d) out of part(%d,%d),(%d,%d)\n",
>> +                       loc.col, loc.row,
>> +                       apart->range.start.col, apart->range.start.row,
>> +                       apart->range.size.col, apart->range.size.row);
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (!is_write)
>> +               return 0;
>> +
>> +       regend32 = lower_32_bits(regend64);
>> +       ttype = adev->ops->get_tile_type(&loc);
>> +       for (i = 0; i < adev->num_kernel_regs; i++) {
>> +               const struct aie_tile_regs *regs;
>> +               u32 rttype, writable;
>> +
>> +               regs = &adev->kernel_regs[i];
>> +               rttype = (regs->attribute & AIE_REGS_ATTR_TILE_TYPE_MASK) >>
>> +                        AIE_REGS_ATTR_TILE_TYPE_SHIFT;
>> +               writable = (regs->attribute & AIE_REGS_ATTR_PERM_MASK) >>
>> +                          AIE_REGS_ATTR_PERM_SHIFT;
>> +               if (!(ttype & rttype))
>> +                       continue;
>> +               if ((regoff >= regs->soff && regoff <= regs->eoff) ||
>> +                   (regend32 >= regs->soff && regend32 <= regs->eoff)) {
>> +                       if (!writable) {
>> +                               dev_err(&apart->dev,
>> +                                       "reg 0x%zx,0x%zx not writable.\n",
>> +                                       offset, len);
>> +                               return -EINVAL;
>> +                       }
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +/**
>> + * aie_part_write_register() - AI engine partition write register
>> + * @apart: AI engine partition
>> + * @offset: AI engine register offset
>> + * @len: len of data to write
>> + * @data: data to write
>> + * @mask: mask, if it is non 0, it is mask write.
>> + * @return: number of bytes write for success, or negative value for failure.
>> + *
>> + * This function writes data to the specified registers.
>> + * If the mask is non 0, it is mask write.
>> + */
>> +static int aie_part_write_register(struct aie_partition *apart, size_t
>> offset,
>> +                                  size_t len, void *data, u32 mask)
>> +{
>> +       int ret;
>> +       void __iomem *va;
>> +
>> +       if (mask && len > sizeof(u32)) {
>> +               /* For mask write, only allow 32bit. */
>> +               dev_err(&apart->dev,
>> +                       "failed mask write, len is more that 32bit.\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       /* offset is expected to be relative to the start of the partition */
>> +       offset += aie_cal_regoff(apart->adev, apart->range.start, 0);
>> +       ret = aie_part_reg_validation(apart, offset, len, 1);
>> +       if (ret < 0) {
>> +               dev_err(&apart->dev, "failed to write to 0x%zx,0x%zx.\n",
>> +                       offset, len);
>> +               return ret;
>> +       }
>> +
>> +       va = apart->adev->base + offset;
>> +       if (!mask) {
>> +               if (len == sizeof(u32))
>> +                       iowrite32(*((u32 *)data),  va);
>> +               else
>> +                       memcpy_toio(va, data, len);
>> +       } else {
>> +               u32 val = ioread32(va);
>> +
>> +               val &= ~mask;
>> +               val |= *((u32 *)data) & mask;
>> +               iowrite32(val, va);
>> +       }
>> +
>> +       return (int)len;
>> +}
>> +
>> +/**
>> + * aie_part_access_regs() - AI engine partition registers access
>> + * @apart: AI engine partition
>> + * @num_reqs: number of access requests
>> + * @reqs: array of registers access
>> + * @return: 0 for success, and negative value for failure.
>> + *
>> + * This function executes AI engine partition register access requests.
>> + */
>> +static int aie_part_access_regs(struct aie_partition *apart, u32 num_reqs,
>> +                               struct aie_reg_args *reqs)
>> +{
>> +       u32 i;
>> +
>> +       for (i = 0; i < num_reqs; i++) {
>> +               struct aie_reg_args *args = &reqs[i];
>> +               int ret;
>> +
>> +               if (args->op != AIE_REG_WRITE) {
>> +                       dev_err(&apart->dev,
>> +                               "Invalid register command type: %u.\n",
>> +                               args->op);
>> +                       return -EINVAL;
>> +               }
>> +               ret = aie_part_write_register(apart,
>> +                                             (size_t)args->offset,
>> +                                             sizeof(args->val),
>> +                                             &args->val, args->mask);
>> +               if (ret < 0) {
>> +                       dev_err(&apart->dev, "reg op %u failed: 0x%llx.\n",
>> +                               args->op, args->offset);
>> +                       return ret;
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int aie_part_release(struct inode *inode, struct file *filp)
>> +{
>> +       struct aie_partition *apart = filp->private_data;
>> +       int ret;
>> +
>> +       /*
>> +        * TODO: It will need to reset the SHIM columns and gate the
>> +        * tiles of the partition.
>> +        */
>> +       ret = mutex_lock_interruptible(&apart->mlock);
>> +       if (ret)
>> +               return ret;
>> +
>> +       apart->status = 0;
>> +       mutex_unlock(&apart->mlock);
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct vm_operations_struct aie_part_physical_vm_ops = {
>> +#ifdef CONFIG_HAVE_IOREMAP_PROT
>> +       .access = generic_access_phys,
>> +#endif
>> +};
>> +
>> +static int aie_part_mmap(struct file *fp, struct vm_area_struct *vma)
>> +{
>> +       struct aie_partition *apart = fp->private_data;
>> +       struct aie_device *adev = apart->adev;
>> +       unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
>> +       phys_addr_t addr;
>> +       size_t size;
>> +
>> +       if (vma->vm_end < vma->vm_start)
>> +               return -EINVAL;
>> +       /* Only allow userspace directly read registers */
>> +       if (vma->vm_flags & VM_WRITE) {
>> +               dev_err(&apart->dev, "%s: do not support writable mmap.\n",
>> +                       __func__);
>> +               return -EINVAL;
>> +       }
>> +       vma->vm_private_data = apart;
>> +       vma->vm_ops = &aie_part_physical_vm_ops;
>> +       size = apart->range.size.col << adev->col_shift;
>> +       if ((vma->vm_end - vma->vm_start) > (size - offset)) {
>> +               dev_err(&apart->dev,
>> +                       "%s: size exceed.\n", __func__);
>> +               return -EINVAL;
>> +       }
>> +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
>> +       /* Calculate the partition address */
>> +       addr = adev->res->start;
>> +       addr += apart->range.start.col << adev->col_shift;
>> +       addr += apart->range.start.row << adev->row_shift;
>> +       addr += offset;
>> +       return remap_pfn_range(vma,
>> +                              vma->vm_start,
>> +                              addr >> PAGE_SHIFT,
>> +                              vma->vm_end - vma->vm_start,
>> +                              vma->vm_page_prot);
>> +}
>> +
>> +static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long
>> arg)
>> +{
>> +       struct aie_partition *apart = fp->private_data;
>> +       void __user *argp = (void __user *)arg;
>> +       long ret;
>> +
>> +       switch (cmd) {
>> +       case AIE_REG_IOCTL:
>> +       {
>> +               struct aie_reg_args raccess;
>> +
>> +               if (copy_from_user(&raccess, argp, sizeof(raccess)))
>> +                       return -EFAULT;
>> +
>> +               ret = mutex_lock_interruptible(&apart->mlock);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               ret = aie_part_access_regs(apart, 1, &raccess);
>> +               mutex_unlock(&apart->mlock);
>> +               break;
>> +       }
>> +       default:
>> +               dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd);
>> +               ret = -EINVAL;
>> +               break;
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +const struct file_operations aie_part_fops = {
>> +       .owner          = THIS_MODULE,
>> +       .release        = aie_part_release,
>> +       .mmap           = aie_part_mmap,
>> +       .unlocked_ioctl = aie_part_ioctl,
>> +};
>> +
>> +/**
>> + * aie_part_release_device() - release an AI engine partition instance
>> + * @dev: AI engine partition device
>> + *
>> + * It will be called by device driver core when no one holds a valid
>> + * pointer to @dev anymore.
>> + */
>> +static void aie_part_release_device(struct device *dev)
>> +{
>> +       struct aie_partition *apart = dev_to_aiepart(dev);
>> +       struct aie_device *adev = apart->adev;
>> +       int ret;
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret) {
>> +               dev_warn(&apart->dev,
>> +                        "getting adev->mlock is interrupted by signal\n");
>> +       }
>> +
>> +       aie_resource_put_region(&adev->cols_res, apart->range.start.col,
>> +                               apart->range.size.col);
>> +       list_del(&apart->node);
>> +       mutex_unlock(&adev->mlock);
>> +       put_device(apart->dev.parent);
>> +}
>> +
>> +/**
>> + * aie_create_partition() - create AI engine partition instance
>> + * @adev: AI engine device
>> + * @range: AI engine partition range to check. A range describes a group
>> + *        of AI engine tiles.
>> + * @return: created AI engine partition pointer for success, and PTR_ERR
>> + *         for failure.
>> + *
>> + * This function creates an AI engine partition instance.
>> + * It creates AI engine partition, the AI engine partition device and
>> + * the AI engine partition character device.
>> + */
>> +static struct aie_partition *aie_create_partition(struct aie_device *adev,
>> +                                                 struct aie_range *range)
>> +{
>> +       struct aie_partition *apart;
>> +       struct device *dev;
>> +       char devname[32];
>> +       int ret;
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret)
>> +               return ERR_PTR(ret);
>> +
>> +       ret = aie_resource_check_region(&adev->cols_res, range->start.col,
>> +                                       range->size.col);
>> +       if (ret != range->start.col) {
>> +               dev_err(&adev->dev, "invalid partition (%u,%u)(%u,%u).\n",
>> +                       range->start.col, range->start.row,
>> +                       range->size.col, range->size.row);
>> +               mutex_unlock(&adev->mlock);
>> +               return ERR_PTR(-EINVAL);
>> +       }
>> +       ret = aie_resource_get_region(&adev->cols_res, range->start.col,
>> +                                     range->size.col);
>> +       if (ret != range->start.col) {
>> +               dev_err(&adev->dev, "failed to get partition
>> (%u,%u)(%u,%u).\n",
>> +                       range->start.col, range->start.row,
>> +                       range->size.col, range->size.row);
>> +               mutex_unlock(&adev->mlock);
>> +               return ERR_PTR(-EFAULT);
>> +       }
>> +       mutex_unlock(&adev->mlock);
>> +
>> +       apart = devm_kzalloc(&adev->dev, sizeof(*apart), GFP_KERNEL);
>> +       if (!apart)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       apart->adev = adev;
>> +       memcpy(&apart->range, range, sizeof(*range));
>> +       mutex_init(&apart->mlock);
>> +
>> +       /* Create AI engine partition device */
>> +       dev = &apart->dev;
>> +       device_initialize(dev);
>> +       dev->parent = &adev->dev;
>> +       dev->class = aie_class;
>> +       dev_set_drvdata(dev, apart);
>> +       snprintf(devname, sizeof(devname) - 1, "aiepart_%d_%d",
>> +                apart->range.start.col, apart->range.size.col);
>> +       dev_set_name(dev, devname);
>> +       /* We can now rely on the release function for cleanup */
>> +       dev->release = aie_part_release_device;
>> +       ret = device_add(dev);
>> +       if (ret) {
>> +               dev_err(dev, "device_add failed: %d\n", ret);
>> +               put_device(dev);
>> +               return ERR_PTR(ret);
>> +       }
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret) {
>> +               put_device(dev);
>> +               return ERR_PTR(ret);
>> +       }
>> +
>> +       list_add_tail(&apart->node, &adev->partitions);
>> +       mutex_unlock(&adev->mlock);
>> +       get_device(&adev->dev);
>> +       dev_dbg(dev, "created AIE partition device.\n");
>> +
>> +       return apart;
>> +}
>> +
>> +struct aie_partition *
>> +of_aie_part_probe(struct aie_device *adev, struct device_node *nc)
>> +{
>> +       struct aie_partition *apart;
>> +       struct aie_range range;
>> +       u32 partition_id, regs[4];
>> +       int ret;
>> +
>> +       /* Select device driver */
>> +       ret = of_property_read_u32_array(nc, "reg", regs, ARRAY_SIZE(regs));
>> +       if (ret < 0) {
>> +               dev_err(&adev->dev,
>> +                       "probe %pOF failed, no tiles range information.\n",
>> +                       nc);
>> +               return ERR_PTR(ret);
>> +       }
>> +       range.start.col = regs[0];
>> +       range.start.row = regs[1];
>> +       range.size.col = regs[2];
>> +       range.size.row = regs[3];
>> +
>> +       ret = of_property_read_u32_index(nc, "xlnx,partition-id", 0,
>> +                                        &partition_id);
>> +       if (ret < 0) {
>> +               dev_err(&adev->dev,
>> +                       "probe %pOF failed, no partition id.\n", nc);
>> +               return ERR_PTR(ret);
>> +       }
>> +
>> +       ret = mutex_lock_interruptible(&adev->mlock);
>> +       if (ret)
>> +               return ERR_PTR(ret);
>> +
>> +       apart = aie_get_partition_from_id(adev, partition_id);
>> +       mutex_unlock(&adev->mlock);
>> +       if (apart) {
>> +               dev_err(&adev->dev,
>> +                       "probe failed: partition %u exists.\n",
>> +                       partition_id);
>> +               return ERR_PTR(ret);
>> +       }
>> +
>> +       apart = aie_create_partition(adev, &range);
>> +       if (IS_ERR(apart)) {
>> +               dev_err(&adev->dev,
>> +                       "%s: failed to create part(%u,%u),(%u,%u).\n",
>> +                       __func__, range.start.col, range.start.row,
>> +                       range.size.col, range.size.row);
>> +               return apart;
>> +       }
>> +
>> +       of_node_get(nc);
>> +       apart->dev.of_node = nc;
>> +       apart->partition_id = partition_id;
>> +
>> +       dev_info(&adev->dev,
>> +                "AI engine part(%u,%u),(%u,%u), id %u is probed
>> successfully.\n",
>> +                range.start.col, range.start.row,
>> +                range.size.col, range.size.row, apart->partition_id);
>> +
>> +       return apart;
>> +}
>> +
>> +/**
>> + * aie_destroy_part() - destroy AI engine partition
>> + * @apart: AI engine partition
>> + *
>> + * This function will remove AI engine partition.
>> + */
>> +void aie_part_remove(struct aie_partition *apart)
>> +{
>> +       device_del(&apart->dev);
>> +       put_device(&apart->dev);
>> +}
>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-res.c
>> b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
>> new file mode 100644
>> index 0000000..36f08bf
>> --- /dev/null
>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
>> @@ -0,0 +1,114 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Xilinx AI Engine device driver
>> + *
>> + * Copyright (C) 2020 Xilinx, Inc.
>> + */
>> +
>> +#include <linux/bitmap.h>
>> +
>> +#include "ai-engine-internal.h"
>> +
>> +/**
>> + * aie_resource_initialize() - initialize AI engine resource
>> + * @res: pointer to AI engine resource
>> + * @count: total number of element of this resource
>> + * @return: 0 for success, negative value for failure.
>> + *
>> + * This function will initialize the data structure for the
>> + * resource.
>> + */
>> +int aie_resource_initialize(struct aie_resource *res, int count)
>> +{
>> +       if (!res || !count)
>> +               return -EINVAL;
>> +       res->bitmap = bitmap_zalloc(count, GFP_KERNEL);
>> +       if (!res->bitmap)
>> +               return -ENOMEM;
>> +       res->total = count;
>> +
>> +       return 0;
>> +}
>> +
>> +/**
>> + * aie_resource_uninitialize() - uninitialize AI engine resource
>> + * @res: pointer to AI engine resource
>> + *
>> + * This function will release the AI engine resource data members.
>> + */
>> +void aie_resource_uninitialize(struct aie_resource *res)
>> +{
>> +       res->total = 0;
>> +       if (res->bitmap)
>> +               bitmap_free(res->bitmap);
>> +}
>> +
>> +/**
>> + * aie_resource_check() - check availability of requested resource
>> + * @res: pointer to AI engine resource to check
>> + * @start: start index of the required resource, it will only be used if
>> + *        @continuous is 1. It will check the available resource starting
>> from
>> + *        @start
>> + * @count: number of requested element
>> + * @return: start resource id if the requested number of resources are
>> available
>> + *         It will return negative value of errors.
>> + *
>> + * This function will check the availability. It will return start resource
>> id
>> + * if the requested number of resources are available.
>> + */
>> +int aie_resource_check_region(struct aie_resource *res,
>> +                             u32 start, u32 count)
>> +{
>> +       unsigned long id;
>> +
>> +       if (!res || !res->bitmap || !count)
>> +               return -EINVAL;
>> +       id = bitmap_find_next_zero_area(res->bitmap, res->total, start,
>> +                                       count, 0);
>> +       if (id >= res->total)
>> +               return -ERANGE;
>> +
>> +       return (int)id;
>> +}
>> +
>> +/**
>> + * aie_resource_get_region() - get requested AI engine resource
>> + * @res: pointer to AI engine resource to check
>> + * @count: number of requested element
>> + * @start: start index of the required resource
>> + * @return: start resource id for success, and negative value for failure.
>> + *
>> + * This function check if the requested AI engine resource is available.
>> + * If it is available, mark it used and return the start resource id.
>> + */
>> +int aie_resource_get_region(struct aie_resource *res, u32 start, u32 count)
>> +{
>> +       unsigned long off;
>> +
>> +       if (!res || !res->bitmap || !count)
>> +               return -EINVAL;
>> +       off = bitmap_find_next_zero_area(res->bitmap, res->total, start,
>> +                                        count, 0);
>> +       if (off >= res->total) {
>> +               pr_err("Failed to get available AI engine resource.\n");
>> +               return -ERANGE;
>> +       }
>> +       bitmap_set(res->bitmap, off, count);
>> +
>> +       return (int)off;
>> +}
>> +
>> +/**
>> + * aie_resource_put_region() - release requested AI engine resource
>> + * @res: pointer to AI engine resource to check
>> + * @start: start index of the resource to release
>> + * @count: number of elements to release
>> + *
>> + * This function release the requested AI engine resource.
>> + */
>> +void aie_resource_put_region(struct aie_resource *res, int start, u32 count)
>> +{
>> +       if (!res || !count)
>> +               return;
>> +       bitmap_clear(res->bitmap, start, count);
>> +}
>> diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-
>> engine.h
>> new file mode 100644
>> index 0000000..acbc781
>> --- /dev/null
>> +++ b/include/uapi/linux/xlnx-ai-engine.h
>> @@ -0,0 +1,107 @@
>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>> +/*
>> + * Copyright (c) 2020, Xilinx Inc.
>> + */
>> +
>> +#ifndef _UAPI_AI_ENGINE_H_
>> +#define _UAPI_AI_ENGINE_H_
>> +
>> +#include <linux/ioctl.h>
>> +#include <linux/types.h>
>> +
>> +enum aie_reg_op {
>> +       AIE_REG_WRITE,
>> +};
>> +
>> +/* AI engine partition is in use */
>> +#define XAIE_PART_STATUS_INUSE         (1U << 0)
>> +
>> +/**
>> + * struct aie_location - AIE location information
>> + * @col: column id
>> + * @row: row id
>> + */
>> +struct aie_location {
>> +       __u32 col;
>> +       __u32 row;
>> +};
>> +
>> +/**
>> + * struct aie_range - AIE range information
>> + * @start: start tile location
>> + * @size: size of the range, number of columns and rows
>> + */
>> +struct aie_range {
>> +       struct aie_location start;
>> +       struct aie_location size;
>> +};
>> +
>> +/**
>> + * struct aie_reg_args - AIE access register arguments
>> + * @op: if this request is to read, write or poll register
>> + * @mask: mask for mask write, 0 for not mask write
>> + * @offset: offset of register to the start of an AI engine partition
>> + * @val: value to write or get
>> + */
>> +struct aie_reg_args {
>> +       enum aie_reg_op op;
>> +       __u32 mask;
>> +       __u64 offset;
>> +       __u32 val;
>> +};
>> +
>> +/**
>> + * struct aie_range_args - AIE range request arguments
>> + * @partition_id: partition id. It is used to identify the
>> + *               AI engine partition in the system.
>> + * @uid: image identifier loaded on the AI engine partition
>> + * @range: range of AIE tiles
>> + * @status: indicate if the AI engine is in use.
>> + *         0 means not in used, otherwise, in use.
>> + */
>> +struct aie_range_args {
>> +       __u32 partition_id;
>> +       __u32 uid;
>> +       struct aie_range range;
>> +       __u32 status;
>> +};
>> +
>> +/**
>> + * struct aie_partition_query - AIE partition query arguments
>> + * @partition_cnt: number of defined partitions in the system
>> + * @partitions: buffer to store defined partitions information.
>> + */
>> +struct aie_partition_query {
>> +       struct aie_range_args *partitions;
>> +       __u32 partition_cnt;
>> +};
>> +
>> +/**
>> + * struct aie_partition_req - AIE request partition arguments
>> + * @partition_id: partition node id. It is used to identify the AI engine
>> + *               partition in the system.
>> + * @uid: image identifier loaded on the AI engine partition
>> + * @meta_data: meta data to indicate which resources used by application.
>> + * @flag: used for application to indicate particular driver requirements
>> + *       application wants to have for the partition. e.g. do not clean
>> + *       resource when closing the partition.
>> + */
>> +struct aie_partition_req {
>> +       __u32 partition_id;
>> +       __u32 uid;
>> +       __u64 meta_data;
>> +       __u32 flag;
>> +};
>> +
>> +#define AIE_IOCTL_BASE 'A'
>> +
>> +/* AI engine device IOCTL operations */
>> +#define AIE_ENQUIRE_PART_IOCTL         _IOWR(AIE_IOCTL_BASE, 0x1, \
>> +                                             struct aie_partition_query)
>> +#define AIE_REQUEST_PART_IOCTL         _IOR(AIE_IOCTL_BASE, 0x2, \
>> +                                            struct aie_partition_req)
>> +
>> +/* AI engine partition IOCTL operations */
>> +#define AIE_REG_IOCTL                  _IOWR(AIE_IOCTL_BASE, 0x8, \
>> +                                             struct aie_reg_args)
>> +#endif
>

2020-12-09 01:03:04

by Nicolas Dufresne

[permalink] [raw]
Subject: Re: [PATCH 2/9] misc: Add Xilinx AI engine device driver

Le mercredi 18 novembre 2020 à 00:06 -0800, Wendy Liang a écrit :
> Create AI engine device/partition hierarchical structure.
>
> Each AI engine device can have multiple logical partitions(groups of AI
> engine tiles). Each partition is column based and has its own node ID
> in the system. AI engine device driver manages its partitions.
>
> Applications can access AI engine partition through the AI engine
> partition driver instance. AI engine registers write is moved to kernel
> as there are registers in the AI engine array needs privilege
> permission.

Hi there, it's nice to see an effort to upstream an AI driver. I'm a little
worried this driver is not obvious to use from it's source code itself. So you
have reference to some Open Source code that demonstrate it's usage ?

>
> Signed-off-by: Wendy Liang <[email protected]>
> Signed-off-by: Hyun Kwon <[email protected]>
> ---
>  MAINTAINERS                                        |   8 +
>  drivers/misc/Kconfig                               |  12 +
>  drivers/misc/Makefile                              |   1 +
>  drivers/misc/xilinx-ai-engine/Makefile             |  11 +
>  drivers/misc/xilinx-ai-engine/ai-engine-aie.c      | 115 +++++
>  drivers/misc/xilinx-ai-engine/ai-engine-dev.c      | 448 ++++++++++++++++++
>  drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 226 ++++++++++
>  drivers/misc/xilinx-ai-engine/ai-engine-part.c     | 498
> +++++++++++++++++++++
>  drivers/misc/xilinx-ai-engine/ai-engine-res.c      | 114 +++++
>  include/uapi/linux/xlnx-ai-engine.h                | 107 +++++
>  10 files changed, 1540 insertions(+)
>  create mode 100644 drivers/misc/xilinx-ai-engine/Makefile
>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-part.c
>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-res.c
>  create mode 100644 include/uapi/linux/xlnx-ai-engine.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5cc595a..40e3351 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19283,6 +19283,14 @@ T:     git https://github.com/Xilinx/linux-xlnx.git
>  F:     Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml
>  F:     drivers/phy/xilinx/phy-zynqmp.c
>  
> +XILINX AI ENGINE DRIVER
> +M:     Wendy Liang <[email protected]>
> +S:     Supported
> +F:     Documentation/devicetree/bindings/soc/xilinx/xlnx,ai-engine.yaml
> +F:     drivers/misc/xilinx-ai-engine/
> +F:     include/linux/xlnx-ai-engine.h
> +F:     include/uapi/linux/xlnx-ai-engine.h
> +
>  XILLYBUS DRIVER
>  M:     Eli Billauer <[email protected]>
>  L:     [email protected]
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index fafa8b0..0b8ce4d 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -444,6 +444,18 @@ config XILINX_SDFEC
>  
>           If unsure, say N.
>  
> +config XILINX_AIE
> +       tristate "Xilinx AI engine"
> +       depends on ARM64 || COMPILE_TEST
> +       help
> +         This option enables support for the Xilinx AI engine driver.
> +         One Xilinx AI engine device can have multiple partitions (groups of
> +         AI engine tiles). Xilinx AI engine device driver instance manages
> +         AI engine partitions. User application access its partitions through
> +         AI engine partition instance file operations.
> +
> +         If unsure, say N
> +
>  config MISC_RTSX
>         tristate
>         default MISC_RTSX_PCI || MISC_RTSX_USB
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index d23231e..2176b18 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -57,3 +57,4 @@ obj-$(CONFIG_HABANA_AI)               += habanalabs/
>  obj-$(CONFIG_UACCE)            += uacce/
>  obj-$(CONFIG_XILINX_SDFEC)     += xilinx_sdfec.o
>  obj-$(CONFIG_HISI_HIKEY_USB)   += hisi_hikey_usb.o
> +obj-$(CONFIG_XILINX_AIE)       += xilinx-ai-engine/
> diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-
> engine/Makefile
> new file mode 100644
> index 0000000..7827a0a
> --- /dev/null
> +++ b/drivers/misc/xilinx-ai-engine/Makefile
> @@ -0,0 +1,11 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Makefile for Xilinx AI engine device driver
> +#
> +
> +obj-$(CONFIG_XILINX_AIE)       += xilinx-aie.o
> +
> +xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \
> +                                  ai-engine-dev.o \
> +                                  ai-engine-part.o \
> +                                  ai-engine-res.o
> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
> b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
> new file mode 100644
> index 0000000..319260f
> --- /dev/null
> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
> @@ -0,0 +1,115 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Xilinx AI Engine driver AIE device specific implementation
> + *
> + * Copyright (C) 2020 Xilinx, Inc.
> + */
> +
> +#include <linux/slab.h>
> +
> +#include "ai-engine-internal.h"
> +
> +#define AIE_ARRAY_SHIFT                30U
> +#define AIE_COL_SHIFT          23U
> +#define AIE_ROW_SHIFT          18U
> +
> +/*
> + * Registers offsets
> + */
> +#define AIE_SHIMNOC_L2INTR_MASK_REGOFF         0x00015000U
> +#define AIE_SHIMNOC_L2INTR_INTR_REGOFF         0x00015010U
> +#define AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF     0x0001d000U
> +#define AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF     0x0001d13cU
> +#define AIE_SHIMNOC_AXIMM_REGOFF               0x0001e020U
> +#define AIE_SHIMPL_L1INTR_MASK_A_REGOFF                0x00035000U
> +#define AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF 0x00035050U
> +#define AIE_SHIMPL_CLKCNTR_REGOFF              0x00036040U
> +#define AIE_SHIMPL_RESET_REGOFF                        0x0003604cU
> +#define AIE_TILE_CORE_CLKCNTR_REGOFF           0x00036040U
> +
> +static const struct aie_tile_regs aie_kernel_regs[] = {
> +       /* SHIM AXI MM Config */
> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> +        .soff = AIE_SHIMNOC_AXIMM_REGOFF,
> +        .eoff = AIE_SHIMNOC_AXIMM_REGOFF,
> +       },
> +       /* SHIM DMA ADDRESS range */
> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> +        .soff = AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF,
> +        .eoff = AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF,
> +       },
> +       /* SHIM 2nd level interrupt controller */
> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> +        .soff = AIE_SHIMNOC_L2INTR_MASK_REGOFF,
> +        .eoff = AIE_SHIMNOC_L2INTR_INTR_REGOFF,
> +       },
> +       /* SHIM 1st level interrupt controller */
> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> +        .soff = AIE_SHIMPL_L1INTR_MASK_A_REGOFF,
> +        .eoff = AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF,
> +       },
> +       /* SHIM reset Enable */
> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> +        .soff = AIE_SHIMPL_RESET_REGOFF,
> +        .eoff = AIE_SHIMPL_RESET_REGOFF,
> +       },
> +       /* SHIM clock control */
> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> +        .soff = AIE_SHIMPL_CLKCNTR_REGOFF,
> +        .eoff = AIE_SHIMPL_CLKCNTR_REGOFF,
> +       },
> +       /* Tile clock control */
> +       {.attribute = AIE_TILE_TYPE_TILE << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> +        .soff = AIE_TILE_CORE_CLKCNTR_REGOFF,
> +        .eoff = AIE_TILE_CORE_CLKCNTR_REGOFF,
> +       },
> +};
> +
> +static u32 aie_get_tile_type(struct aie_location *loc)
> +{
> +       if (loc->row)
> +               return AIE_TILE_TYPE_TILE;
> +       /* SHIM row */
> +       if ((loc->col % 4) < 2)
> +               return AIE_TILE_TYPE_SHIMPL;
> +
> +       return AIE_TILE_TYPE_SHIMNOC;
> +}
> +
> +static const struct aie_tile_operations aie_ops = {
> +       .get_tile_type = aie_get_tile_type,
> +};
> +
> +/**
> + * aie_device_init() - Initialize AI engine device struct AIE specific
> + *                    properties
> + * @adev: AI engine device
> + * @return: 0 for success, negative value for failure.
> + *
> + * This function initialize the AI engine device structure device version
> + * specific elements such as register addressing related array shift,
> + * column shift, and row shift; AIE specific device operations, device
> + * columns resource.
> + */
> +int aie_device_init(struct aie_device *adev)
> +{
> +       int ret;
> +
> +       adev->array_shift = AIE_ARRAY_SHIFT;
> +       adev->col_shift = AIE_COL_SHIFT;
> +       adev->row_shift = AIE_ROW_SHIFT;
> +       adev->ops = &aie_ops;
> +       adev->num_kernel_regs = ARRAY_SIZE(aie_kernel_regs);
> +       adev->kernel_regs = aie_kernel_regs;
> +
> +       /* Get the columns resource */
> +       /* Get number of columns from AI engine memory resource */
> +       ret = aie_resource_initialize(&adev->cols_res, 50);
> +       if (ret)
> +               dev_err(&adev->dev, "failed to initialize columns
> resource.\n");
> +
> +       return ret;
> +}
> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
> b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
> new file mode 100644
> index 0000000..2ab2dc8
> --- /dev/null
> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
> @@ -0,0 +1,448 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Xilinx AI Engine device driver
> + *
> + * Copyright (C) 2020 Xilinx, Inc.
> + */
> +
> +#include <linux/anon_inodes.h>
> +#include <linux/cdev.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/file.h>
> +#include <linux/fs.h>
> +#include <linux/idr.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <uapi/linux/xlnx-ai-engine.h>
> +
> +#include "ai-engine-internal.h"
> +
> +#define AIE_DEV_MAX    (MINORMASK + 1)
> +
> +static dev_t aie_major;
> +struct class *aie_class;
> +
> +static DEFINE_IDA(aie_device_ida);
> +static DEFINE_IDA(aie_minor_ida);
> +
> +/**
> + * aie_get_partition_fd() - Get AI engine partition file descriptor
> + * @apart: AI engine partition
> + * @return: file descriptor for AI engine partition for success, or negative
> + *         value for failure.
> + *
> + * This function gets a file descriptor for the AI engine partition.
> + */
> +static int aie_get_partition_fd(struct aie_partition *apart)
> +{
> +       struct file *filep;
> +       int ret;
> +
> +       /*
> +        * We can't use anon_inode_getfd() because we need to modify
> +        * the f_mode flags directly to allow more than just ioctls
> +        */
> +       ret = get_unused_fd_flags(O_CLOEXEC);
> +       if (ret < 0)
> +               return ret;
> +
> +       filep = anon_inode_getfile(dev_name(&apart->dev), &aie_part_fops,
> +                                  apart, O_RDWR);
> +       if (IS_ERR(filep)) {
> +               put_unused_fd(ret);
> +               ret = PTR_ERR(filep);
> +               return ret;
> +       }
> +       filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
> +       fd_install(ret, filep);
> +
> +       return ret;
> +}
> +
> +/**
> + * aie_enquire_partitions() - get AI engine partitions information
> + * @adev: AI engine device
> + * @query: data struct to store the partition information
> + * @return: 0 for success, and negative value for failure.
> + */
> +static int aie_enquire_partitions(struct aie_device *adev,
> +                                 struct aie_partition_query *query)
> +{
> +       struct aie_partition *apart;
> +       u32 partition_cnt, i = 0;
> +       int ret;
> +
> +       if (!query->partitions) {
> +               /*
> +                * If partitions information buffer is NULL.
> +                * It is to get the number of partitions.
> +                */
> +               query->partition_cnt = 0;
> +               list_for_each_entry(apart, &adev->partitions, node)
> +                       query->partition_cnt++;
> +               return 0;
> +       }
> +
> +       partition_cnt = query->partition_cnt;
> +       if (!partition_cnt)
> +               return 0;
> +
> +       ret = mutex_lock_interruptible(&adev->mlock);
> +       if (ret)
> +               return ret;
> +
> +       list_for_each_entry(apart, &adev->partitions, node) {
> +               struct aie_range_args part;
> +
> +               if (i >= partition_cnt)
> +                       break;
> +               part.partition_id = apart->partition_id;
> +               /*
> +                * TBD: check with PLM that if the partition is programmed
> +                * and get the UID of the image which is loaded on the AI
> +                * engine partition.
> +                */
> +               part.uid = 0;
> +               part.range.start.col = apart->range.start.col;
> +               part.range.start.row = apart->range.start.row;
> +               part.range.size.col = apart->range.size.col;
> +               part.range.size.row = apart->range.size.row;
> +               /* Check if partition is in use */
> +               part.status = apart->status;
> +               if (copy_to_user((void __user *)&query->partitions[i], &part,
> +                                sizeof(part))) {
> +                       mutex_unlock(&adev->mlock);
> +                       return -EFAULT;
> +               }
> +               i++;
> +       }
> +       mutex_unlock(&adev->mlock);
> +       query->partition_cnt = i;
> +
> +       return 0;
> +}
> +
> +/**
> + * aie_get_partition_from_id() - get AI engine partition from id
> + * @adev: AI engine device
> + * @partition_id: partition id to check
> + * @return: partition pointer if partition exists, otherwise, NULL.
> + *
> + * This function checks defined partitions with partition id.
> + * This function expect the caller to lock mlock of @adev.
> + */
> +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
> +                                               u32 partition_id)
> +{
> +       struct aie_partition *apart;
> +
> +       list_for_each_entry(apart, &adev->partitions, node) {
> +               if (apart->partition_id == partition_id)
> +                       return apart;
> +       }
> +
> +       return NULL;
> +}
> +
> +/**
> + * aie_request_partition() - request AI engine partition
> + * @adev: AI engine device
> + * @req: partition request, includes the requested AI engine information
> + *      such as partition node ID and the UID of the image which is
> + *      loaded on the partition.
> + * @return: partition pointer if partition exists, otherwise, NULL.
> + *
> + * This function finds a defined partition which matches the specified
> + * partition id, request it if it hasn't been requested, and returns it.
> + */
> +struct aie_partition *aie_request_partition(struct aie_device *adev,
> +                                           struct aie_partition_req *req)
> +{
> +       struct aie_partition *apart;
> +       int ret;
> +
> +       ret = mutex_lock_interruptible(&adev->mlock);
> +       if (ret)
> +               return ERR_PTR(ret);
> +
> +       apart = aie_get_partition_from_id(adev, req->partition_id);
> +       if (!apart) {
> +               dev_err(&adev->dev,
> +                       "request partition %u failed, not exist.\n",
> +                       req->partition_id);
> +               mutex_unlock(&adev->mlock);
> +               return ERR_PTR(-EINVAL);
> +       }
> +       /*
> +        * TBD: It will check image UID too to see if the user matches
> +        * what's loaded in the AI engine partition. And check the meta
> +        * data to see which resources used by application.
> +        */
> +
> +       ret = mutex_lock_interruptible(&apart->mlock);
> +       if (ret)
> +               return ERR_PTR(ret);
> +
> +       if (apart->status & XAIE_PART_STATUS_INUSE) {
> +               mutex_unlock(&apart->mlock);
> +               dev_err(&adev->dev,
> +                       "request partition %u failed, partition in use.\n",
> +                       req->partition_id);
> +               apart = ERR_PTR(-EBUSY);
> +       } else {
> +               /*
> +                * TBD:
> +                * 1. setup NOC AXI MM config to only generate error events
> +                *    for slave error and decode error.
> +                * 2. scan to see which tiles have been clock gated.
> +                *
> +                * This needs to be done before the AI engine partition is
> +                * exported for user to access.
> +                */
> +               apart->status = XAIE_PART_STATUS_INUSE;
> +               mutex_unlock(&apart->mlock);
> +       }
> +       mutex_unlock(&adev->mlock);
> +
> +       return apart;
> +}
> +
> +static long xilinx_ai_engine_ioctl(struct file *filp, unsigned int cmd,
> +                                  unsigned long arg)
> +{
> +       struct inode *inode = file_inode(filp);
> +       struct aie_device *adev = cdev_to_aiedev(inode->i_cdev);
> +       void __user *argp = (void __user *)arg;
> +       int ret;
> +
> +       switch (cmd) {
> +       case AIE_ENQUIRE_PART_IOCTL:
> +       {
> +               struct aie_partition_query query;
> +               struct aie_partition_query  __user *uquery_ptr = argp;
> +
> +               if (copy_from_user(&query, uquery_ptr, sizeof(query)))
> +                       return -EFAULT;
> +               ret = aie_enquire_partitions(adev, &query);
> +               if (ret < 0)
> +                       return ret;
> +               if (copy_to_user((void __user *)&uquery_ptr->partition_cnt,
> +                                &query.partition_cnt,
> +                                sizeof(query.partition_cnt)))
> +                       return -EFAULT;
> +               break;
> +       }
> +       case AIE_REQUEST_PART_IOCTL:
> +       {
> +               struct aie_partition_req req;
> +               struct aie_partition *apart;
> +
> +               if (copy_from_user(&req, argp, sizeof(req)))
> +                       return -EFAULT;
> +               apart = aie_request_partition(adev, &req);
> +               if (IS_ERR(apart))
> +                       return PTR_ERR(apart);
> +               ret = aie_get_partition_fd(apart);
> +               if (ret < 0) {
> +                       dev_err(&apart->dev, "failed to get fd.\n");
> +                       break;
> +               }
> +               break;
> +       }
> +       default:
> +               dev_err(&adev->dev, "Invalid ioctl command %u.\n", cmd);
> +               ret = -EINVAL;
> +               break;
> +       }
> +
> +       return ret;
> +}
> +
> +static const struct file_operations aie_device_fops = {
> +       .owner          = THIS_MODULE,
> +       .unlocked_ioctl = xilinx_ai_engine_ioctl,
> +};
> +
> +static void xilinx_ai_engine_release_device(struct device *dev)
> +{
> +       struct aie_device *adev = dev_to_aiedev(dev);
> +
> +       ida_simple_remove(&aie_device_ida, dev->id);
> +       ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
> +       cdev_del(&adev->cdev);
> +       aie_resource_uninitialize(&adev->cols_res);
> +}
> +
> +/**
> + * of_xilinx_ai_engine_part_probe() - probes for AI engine partition nodes
> + * @adev: AI engine device
> + *
> + * This function will probe for children AI engine partition nodes and create
> + * an AI engine partition instance for each node.
> + */
> +static void of_xilinx_ai_engine_part_probe(struct aie_device *adev)
> +{
> +       struct device_node *nc;
> +
> +       for_each_available_child_of_node(adev->dev.of_node, nc) {
> +               struct aie_partition *apart;
> +
> +               if (of_node_test_and_set_flag(nc, OF_POPULATED))
> +                       continue;
> +               apart = of_aie_part_probe(adev, nc);
> +               if (IS_ERR(apart)) {
> +                       dev_err(&adev->dev,
> +                               "Failed to probe AI engine part for %pOF\n",
> +                               nc);
> +                       of_node_clear_flag(nc, OF_POPULATED);
> +               }
> +       }
> +}
> +
> +static int xilinx_ai_engine_probe(struct platform_device *pdev)
> +{
> +       struct aie_device *adev;
> +       struct device *dev;
> +       int ret;
> +
> +       adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
> +       if (!adev)
> +               return -ENOMEM;
> +       platform_set_drvdata(pdev, adev);
> +       INIT_LIST_HEAD(&adev->partitions);
> +       mutex_init(&adev->mlock);
> +
> +       adev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!adev->res) {
> +               dev_err(&pdev->dev, "No memory resource.\n");
> +               return -EINVAL;
> +       }
> +       adev->base = devm_ioremap_resource(&pdev->dev, adev->res);
> +       if (IS_ERR(adev->base)) {
> +               dev_err(&pdev->dev, "no io memory resource.\n");
> +               return PTR_ERR(adev->base);
> +       }
> +
> +       /* Initialize AIE device specific instance. */
> +       ret = aie_device_init(adev);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "failed to initialize device
> instance.\n");
> +               return ret;
> +       }
> +
> +       dev = &adev->dev;
> +       device_initialize(dev);
> +       dev->class = aie_class;
> +       dev->parent = &pdev->dev;
> +       dev->of_node = pdev->dev.of_node;
> +
> +       ret = ida_simple_get(&aie_minor_ida, 0, AIE_DEV_MAX, GFP_KERNEL);
> +       if (ret < 0)
> +               goto free_dev;
> +       dev->devt = MKDEV(MAJOR(aie_major), ret);
> +       ret = ida_simple_get(&aie_device_ida, 0, 0, GFP_KERNEL);
> +       if (ret < 0)
> +               goto free_minor_ida;
> +       dev->id = ret;
> +       dev_set_name(&adev->dev, "aie%d", dev->id);
> +
> +       cdev_init(&adev->cdev, &aie_device_fops);
> +       adev->cdev.owner = THIS_MODULE;
> +       ret = cdev_add(&adev->cdev, dev->devt, 1);
> +       if (ret)
> +               goto free_ida;
> +       /* We can now rely on the release function for cleanup */
> +       dev->release = xilinx_ai_engine_release_device;
> +
> +       ret = device_add(dev);
> +       if (ret) {
> +               dev_err(&pdev->dev, "device_add failed: %d\n", ret);
> +               put_device(dev);
> +               return ret;
> +       }
> +
> +       of_xilinx_ai_engine_part_probe(adev);
> +       dev_info(&pdev->dev, "Xilinx AI Engine device(cols=%u) probed\n",
> +                adev->cols_res.total);
> +       return 0;
> +
> +free_ida:
> +       ida_simple_remove(&aie_device_ida, dev->id);
> +free_minor_ida:
> +       ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
> +free_dev:
> +       put_device(dev);
> +
> +       return ret;
> +}
> +
> +static int xilinx_ai_engine_remove(struct platform_device *pdev)
> +{
> +       struct aie_device *adev = platform_get_drvdata(pdev);
> +       struct aie_partition *apart;
> +
> +       list_for_each_entry(apart, &adev->partitions, node)
> +               aie_part_remove(apart);
> +
> +       device_del(&adev->dev);
> +       put_device(&adev->dev);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id xilinx_ai_engine_of_match[] = {
> +       { .compatible = "xlnx,ai-engine-v1.0", },
> +       { /* end of table */ },
> +};
> +MODULE_DEVICE_TABLE(of, xilinx_ai_engine_of_match);
> +
> +static struct platform_driver xilinx_ai_engine_driver = {
> +       .probe                  = xilinx_ai_engine_probe,
> +       .remove                 = xilinx_ai_engine_remove,
> +       .driver                 = {
> +               .name           = "xilinx-ai-engine",
> +               .of_match_table = xilinx_ai_engine_of_match,
> +       },
> +};
> +
> +static int __init xilinx_ai_engine_init(void)
> +{
> +       int ret;
> +
> +       ret = alloc_chrdev_region(&aie_major, 0, AIE_DEV_MAX, "aie");
> +       if (ret < 0) {
> +               pr_err("aie: failed to allocate aie region\n");
> +               return ret;
> +       }
> +
> +       aie_class = class_create(THIS_MODULE, "aie");
> +       if (IS_ERR(aie_class)) {
> +               pr_err("failed to create aie class\n");
> +               unregister_chrdev_region(aie_major, AIE_DEV_MAX);
> +               return PTR_ERR(aie_class);
> +       }
> +
> +       platform_driver_register(&xilinx_ai_engine_driver);
> +
> +       return 0;
> +}
> +postcore_initcall(xilinx_ai_engine_init);
> +
> +static void __exit xilinx_ai_engine_exit(void)
> +{
> +       platform_driver_unregister(&xilinx_ai_engine_driver);
> +       class_destroy(aie_class);
> +       unregister_chrdev_region(aie_major, AIE_DEV_MAX);
> +}
> +module_exit(xilinx_ai_engine_exit);
> +
> +MODULE_AUTHOR("Xilinx, Inc.");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
> b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
> new file mode 100644
> index 0000000..6a69946
> --- /dev/null
> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
> @@ -0,0 +1,226 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Xilinx AI Engine driver internal header
> + *
> + * Copyright (C) 2020 Xilinx, Inc.
> + */
> +
> +#ifndef AIE_INTERNAL_H
> +#define AIE_INTERNAL_H
> +
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/cdev.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <uapi/linux/xlnx-ai-engine.h>
> +
> +/*
> + * Macros for AI engine tile type bitmasks
> + */
> +#define AIE_TILE_TYPE_TILE     BIT(0)
> +#define AIE_TILE_TYPE_SHIMPL   BIT(1)
> +/* SHIM NOC tile includes SHIM PL and SHIM NOC modules */
> +#define AIE_TILE_TYPE_SHIMNOC  BIT(2)
> +
> +/*
> + * Macros for attribute property of AI engine registers accessed by kernel
> + * 0 - 7 bits: tile type bits
> + * 8 - 15 bits: permission bits. If it is 1, it allows write from userspace
> + */
> +#define AIE_REGS_ATTR_TILE_TYPE_SHIFT  0U
> +#define AIE_REGS_ATTR_PERM_SHIFT       8U
> +#define AIE_REGS_ATTR_TILE_TYPE_MASK   GENMASK(AIE_REGS_ATTR_PERM_SHIFT - 1,
> \
> +                                               AIE_REGS_ATTR_TILE_TYPE_SHIFT)
> +#define AIE_REGS_ATTR_PERM_MASK                GENMASK(15, \
> +                                               AIE_REGS_ATTR_PERM_SHIFT)
> +
> +/**
> + * struct aie_tile_regs - contiguous range of AI engine register
> + *                       within an AI engine tile
> + * @soff: start offset of the range
> + * @eoff: end offset of the range
> + * @attribute: registers attribute. It uses AIE_REGS_ATTR_* macros defined
> + *            above.
> + */
> +struct aie_tile_regs {
> +       size_t soff;
> +       size_t eoff;
> +       u32 attribute;
> +};
> +
> +struct aie_device;
> +struct aie_partition;
> +
> +/**
> + * struct aie_tile_operations - AI engine device operations
> + * @get_tile_type: get type of tile based on tile operation
> + *
> + * Different AI engine device version has its own device
> + * operation.
> + */
> +struct aie_tile_operations {
> +       u32 (*get_tile_type)(struct aie_location *loc);
> +};
> +
> +/**
> + * struct aie_resource - AI engine resource structure
> + * @bitmap: resource bitmap
> + * @total: total number of resource
> + */
> +struct aie_resource {
> +       unsigned long *bitmap;
> +       u32 total;
> +};
> +
> +/**
> + * struct aie_device - AI engine device structure
> + * @partitions: list of partitions requested
> + * @cdev: cdev for the AI engine
> + * @dev: device for the AI engine device
> + * @mlock: protection for AI engine device operations
> + * @base: AI engine device base virtual address
> + * @res: memory resource of AI engine device
> + * @kernel_regs: array of kernel only registers
> + * @ops: tile operations
> + * @size: size of the AI engine address space
> + * @array_shift: array address shift
> + * @col_shift: column address shift
> + * @row_shift: row address shift
> + * @cols_res: AI engine columns resources to indicate
> + *           while columns are occupied by partitions.
> + * @num_kernel_regs: number of kernel only registers range
> + * @version: AI engine device version
> + */
> +struct aie_device {
> +       struct list_head partitions;
> +       struct cdev cdev;
> +       struct device dev;
> +       struct mutex mlock; /* protection for AI engine partitions */
> +       void __iomem *base;
> +       struct resource *res;
> +       const struct aie_tile_regs *kernel_regs;
> +       const struct aie_tile_operations *ops;
> +       size_t size;
> +       struct aie_resource cols_res;
> +       u32 array_shift;
> +       u32 col_shift;
> +       u32 row_shift;
> +       u32 num_kernel_regs;
> +       int version;
> +};
> +
> +/**
> + * struct aie_partition - AI engine partition structure
> + * @node: list node
> + * @adev: pointer to AI device instance
> + * @range: range of partition
> + * @mlock: protection for AI engine partition operations
> + * @dev: device for the AI engine partition
> + * @partition_id: partition id. Partition ID is the identifier
> + *               of the AI engine partition in the system.
> + * @status: indicate if the partition is in use
> + */
> +struct aie_partition {
> +       struct list_head node;
> +       struct aie_device *adev;
> +       struct aie_range range;
> +       struct mutex mlock; /* protection for AI engine partition operations
> */
> +       struct device dev;
> +       u32 partition_id;
> +       u32 status;
> +};
> +
> +extern struct class *aie_class;
> +extern const struct file_operations aie_part_fops;
> +
> +#define cdev_to_aiedev(i_cdev) container_of((i_cdev), struct aie_device,
> cdev)
> +#define dev_to_aiedev(_dev) container_of((_dev), struct aie_device, dev)
> +#define dev_to_aiepart(_dev) container_of((_dev), struct aie_partition, dev)
> +
> +#define aie_col_mask(adev) ({ \
> +       struct aie_device *_adev = (adev); \
> +       GENMASK_ULL(_adev->array_shift - 1, _adev->col_shift);  \
> +       })
> +
> +#define aie_row_mask(adev) ({ \
> +       struct aie_device *_adev = (adev); \
> +       GENMASK_ULL(_adev->col_shift - 1, _adev->row_shift);  \
> +       })
> +
> +#define aie_tile_reg_mask(adev) ({ \
> +       struct aie_device *_adev = (adev); \
> +       GENMASK_ULL(_adev->row_shift - 1, 0);  \
> +       })
> +
> +/*
> + * Need to define field get, as AI engine shift mask is not constant.
> + * Cannot use FIELD_GET()
> + */
> +#define aie_tile_reg_field_get(mask, shift, regoff) ( \
> +       ((regoff) & (mask)) >> (shift))
> +
> +#define aie_cal_tile_reg(adev, regoff) ( \
> +       aie_tile_reg_field_get(aie_tile_reg_mask(adev), 0, regoff))
> +
> +/**
> + * aie_cal_regoff() - calculate register offset to the whole AI engine
> + *                   device start address
> + * @adev: AI engine device
> + * @loc: AI engine tile location
> + * @regoff_intile: register offset within a tile
> + * @return: register offset to the whole AI engine device start address
> + */
> +static inline u32 aie_cal_regoff(struct aie_device *adev,
> +                                struct aie_location loc, u32 regoff_intile)
> +{
> +       return regoff_intile + (loc.col << adev->col_shift) +
> +              (loc.row << adev->row_shift);
> +}
> +
> +/**
> + * aie_validate_location() - validate tile location within an AI engine
> + *                          partition
> + * @apart: AI engine partition
> + * @loc: AI engine tile location
> + * @return: return 0 if it is valid, negative value for errors.
> + *
> + * This function checks if the AI engine location is within the AI engine
> + * partition.
> + */
> +static inline int aie_validate_location(struct aie_partition *apart,
> +                                       struct aie_location loc)
> +{
> +       if (loc.col < apart->range.start.col ||
> +           loc.col >= apart->range.start.col + apart->range.size.col ||
> +           loc.row < apart->range.start.row ||
> +           loc.row >= apart->range.start.row + apart->range.size.row)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +int aie_resource_initialize(struct aie_resource *res, int count);
> +void aie_resource_uninitialize(struct aie_resource *res);
> +int aie_resource_check_region(struct aie_resource *res, u32 start,
> +                             u32 count);
> +int aie_resource_get_region(struct aie_resource *res, u32 start,
> +                           u32 count);
> +void aie_resource_put_region(struct aie_resource *res, int start, u32 count);
> +
> +const struct file_operations *aie_part_get_fops(void);
> +u8 aie_part_in_use(struct aie_partition *apart);
> +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
> +                                               u32 partition_id);
> +struct aie_partition *aie_request_partition(struct aie_device *adev,
> +                                           struct aie_partition_req *req);
> +struct aie_partition *of_aie_part_probe(struct aie_device *adev,
> +                                       struct device_node *nc);
> +void aie_part_remove(struct aie_partition *apart);
> +
> +int aie_device_init(struct aie_device *adev);
> +#endif /* AIE_INTERNAL_H */
> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c
> b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
> new file mode 100644
> index 0000000..fc8f9f5
> --- /dev/null
> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
> @@ -0,0 +1,498 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Xilinx AI Engine partition driver
> + *
> + * Copyright (C) 2020 Xilinx, Inc.
> + */
> +
> +#include <linux/cdev.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/fs.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/mm.h>
> +#include <linux/mman.h>
> +#include <linux/mmu_context.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/uio.h>
> +#include <uapi/linux/xlnx-ai-engine.h>
> +
> +#include "ai-engine-internal.h"
> +
> +/**
> + * aie_cal_loc() - calculate tile location from register offset to the AI
> + *                engine device
> + * @adev: AI engine device
> + * @loc: memory pointer to restore returning location information
> + * @regoff: tile internal register offset
> + *
> + * This function returns the tile location.
> + */
> +static void aie_cal_loc(struct aie_device *adev,
> +                       struct aie_location *loc, u64 regoff)
> +{
> +       loc->col = (u32)aie_tile_reg_field_get(aie_col_mask(adev),
> +                                              adev->col_shift, regoff);
> +       loc->row = (u32)aie_tile_reg_field_get(aie_row_mask(adev),
> +                                              adev->row_shift, regoff);
> +}
> +
> +/**
> + * aie_part_reg_validation() - validate AI engine partition register access
> + * @apart: AI engine partition
> + * @offset: AI engine register offset
> + * @len: len of data to write/read
> + * @is_write: is the access to write to register
> + * @return: 0 for success, or negative value for failure.
> + *
> + * This function validate if the register to access is within the AI engine
> + * partition. If it is write access, if the register is writable by user.
> + */
> +static int aie_part_reg_validation(struct aie_partition *apart, size_t
> offset,
> +                                  size_t len, u8 is_write)
> +{
> +       struct aie_device *adev;
> +       u32 regend32, ttype;
> +       u64 regoff, regend64;
> +       struct aie_location loc;
> +       unsigned int i;
> +
> +       adev = apart->adev;
> +       if (offset % sizeof(u32)) {
> +               dev_err(&apart->dev,
> +                       "Invalid reg off(0x%zx), not 32bit aligned.\n",
> +                       offset);
> +               return -EINVAL;
> +       }
> +
> +       if (len % sizeof(u32)) {
> +               dev_err(&apart->dev, "Invalid reg operation len %zu.\n", len);
> +               return -EINVAL;
> +       }
> +
> +       regoff = aie_cal_tile_reg(adev, offset);
> +       regend64 = regoff + len;
> +       if (regend64 >= BIT_ULL(adev->row_shift)) {
> +               dev_err(&apart->dev,
> +                       "Invalid reg operation len %zu.\n", len);
> +               return -EINVAL;
> +       }
> +
> +       aie_cal_loc(adev, &loc, offset);
> +       if (aie_validate_location(apart, loc)) {
> +               dev_err(&apart->dev,
> +                       "Invalid (%d,%d) out of part(%d,%d),(%d,%d)\n",
> +                       loc.col, loc.row,
> +                       apart->range.start.col, apart->range.start.row,
> +                       apart->range.size.col, apart->range.size.row);
> +               return -EINVAL;
> +       }
> +
> +       if (!is_write)
> +               return 0;
> +
> +       regend32 = lower_32_bits(regend64);
> +       ttype = adev->ops->get_tile_type(&loc);
> +       for (i = 0; i < adev->num_kernel_regs; i++) {
> +               const struct aie_tile_regs *regs;
> +               u32 rttype, writable;
> +
> +               regs = &adev->kernel_regs[i];
> +               rttype = (regs->attribute & AIE_REGS_ATTR_TILE_TYPE_MASK) >>
> +                        AIE_REGS_ATTR_TILE_TYPE_SHIFT;
> +               writable = (regs->attribute & AIE_REGS_ATTR_PERM_MASK) >>
> +                          AIE_REGS_ATTR_PERM_SHIFT;
> +               if (!(ttype & rttype))
> +                       continue;
> +               if ((regoff >= regs->soff && regoff <= regs->eoff) ||
> +                   (regend32 >= regs->soff && regend32 <= regs->eoff)) {
> +                       if (!writable) {
> +                               dev_err(&apart->dev,
> +                                       "reg 0x%zx,0x%zx not writable.\n",
> +                                       offset, len);
> +                               return -EINVAL;
> +                       }
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * aie_part_write_register() - AI engine partition write register
> + * @apart: AI engine partition
> + * @offset: AI engine register offset
> + * @len: len of data to write
> + * @data: data to write
> + * @mask: mask, if it is non 0, it is mask write.
> + * @return: number of bytes write for success, or negative value for failure.
> + *
> + * This function writes data to the specified registers.
> + * If the mask is non 0, it is mask write.
> + */
> +static int aie_part_write_register(struct aie_partition *apart, size_t
> offset,
> +                                  size_t len, void *data, u32 mask)
> +{
> +       int ret;
> +       void __iomem *va;
> +
> +       if (mask && len > sizeof(u32)) {
> +               /* For mask write, only allow 32bit. */
> +               dev_err(&apart->dev,
> +                       "failed mask write, len is more that 32bit.\n");
> +               return -EINVAL;
> +       }
> +
> +       /* offset is expected to be relative to the start of the partition */
> +       offset += aie_cal_regoff(apart->adev, apart->range.start, 0);
> +       ret = aie_part_reg_validation(apart, offset, len, 1);
> +       if (ret < 0) {
> +               dev_err(&apart->dev, "failed to write to 0x%zx,0x%zx.\n",
> +                       offset, len);
> +               return ret;
> +       }
> +
> +       va = apart->adev->base + offset;
> +       if (!mask) {
> +               if (len == sizeof(u32))
> +                       iowrite32(*((u32 *)data),  va);
> +               else
> +                       memcpy_toio(va, data, len);
> +       } else {
> +               u32 val = ioread32(va);
> +
> +               val &= ~mask;
> +               val |= *((u32 *)data) & mask;
> +               iowrite32(val, va);
> +       }
> +
> +       return (int)len;
> +}
> +
> +/**
> + * aie_part_access_regs() - AI engine partition registers access
> + * @apart: AI engine partition
> + * @num_reqs: number of access requests
> + * @reqs: array of registers access
> + * @return: 0 for success, and negative value for failure.
> + *
> + * This function executes AI engine partition register access requests.
> + */
> +static int aie_part_access_regs(struct aie_partition *apart, u32 num_reqs,
> +                               struct aie_reg_args *reqs)
> +{
> +       u32 i;
> +
> +       for (i = 0; i < num_reqs; i++) {
> +               struct aie_reg_args *args = &reqs[i];
> +               int ret;
> +
> +               if (args->op != AIE_REG_WRITE) {
> +                       dev_err(&apart->dev,
> +                               "Invalid register command type: %u.\n",
> +                               args->op);
> +                       return -EINVAL;
> +               }
> +               ret = aie_part_write_register(apart,
> +                                             (size_t)args->offset,
> +                                             sizeof(args->val),
> +                                             &args->val, args->mask);
> +               if (ret < 0) {
> +                       dev_err(&apart->dev, "reg op %u failed: 0x%llx.\n",
> +                               args->op, args->offset);
> +                       return ret;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static int aie_part_release(struct inode *inode, struct file *filp)
> +{
> +       struct aie_partition *apart = filp->private_data;
> +       int ret;
> +
> +       /*
> +        * TODO: It will need to reset the SHIM columns and gate the
> +        * tiles of the partition.
> +        */
> +       ret = mutex_lock_interruptible(&apart->mlock);
> +       if (ret)
> +               return ret;
> +
> +       apart->status = 0;
> +       mutex_unlock(&apart->mlock);
> +
> +       return 0;
> +}
> +
> +static const struct vm_operations_struct aie_part_physical_vm_ops = {
> +#ifdef CONFIG_HAVE_IOREMAP_PROT
> +       .access = generic_access_phys,
> +#endif
> +};
> +
> +static int aie_part_mmap(struct file *fp, struct vm_area_struct *vma)
> +{
> +       struct aie_partition *apart = fp->private_data;
> +       struct aie_device *adev = apart->adev;
> +       unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
> +       phys_addr_t addr;
> +       size_t size;
> +
> +       if (vma->vm_end < vma->vm_start)
> +               return -EINVAL;
> +       /* Only allow userspace directly read registers */
> +       if (vma->vm_flags & VM_WRITE) {
> +               dev_err(&apart->dev, "%s: do not support writable mmap.\n",
> +                       __func__);
> +               return -EINVAL;
> +       }
> +       vma->vm_private_data = apart;
> +       vma->vm_ops = &aie_part_physical_vm_ops;
> +       size = apart->range.size.col << adev->col_shift;
> +       if ((vma->vm_end - vma->vm_start) > (size - offset)) {
> +               dev_err(&apart->dev,
> +                       "%s: size exceed.\n", __func__);
> +               return -EINVAL;
> +       }
> +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> +       /* Calculate the partition address */
> +       addr = adev->res->start;
> +       addr += apart->range.start.col << adev->col_shift;
> +       addr += apart->range.start.row << adev->row_shift;
> +       addr += offset;
> +       return remap_pfn_range(vma,
> +                              vma->vm_start,
> +                              addr >> PAGE_SHIFT,
> +                              vma->vm_end - vma->vm_start,
> +                              vma->vm_page_prot);
> +}
> +
> +static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long
> arg)
> +{
> +       struct aie_partition *apart = fp->private_data;
> +       void __user *argp = (void __user *)arg;
> +       long ret;
> +
> +       switch (cmd) {
> +       case AIE_REG_IOCTL:
> +       {
> +               struct aie_reg_args raccess;
> +
> +               if (copy_from_user(&raccess, argp, sizeof(raccess)))
> +                       return -EFAULT;
> +
> +               ret = mutex_lock_interruptible(&apart->mlock);
> +               if (ret)
> +                       return ret;
> +
> +               ret = aie_part_access_regs(apart, 1, &raccess);
> +               mutex_unlock(&apart->mlock);
> +               break;
> +       }
> +       default:
> +               dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd);
> +               ret = -EINVAL;
> +               break;
> +       }
> +
> +       return ret;
> +}
> +
> +const struct file_operations aie_part_fops = {
> +       .owner          = THIS_MODULE,
> +       .release        = aie_part_release,
> +       .mmap           = aie_part_mmap,
> +       .unlocked_ioctl = aie_part_ioctl,
> +};
> +
> +/**
> + * aie_part_release_device() - release an AI engine partition instance
> + * @dev: AI engine partition device
> + *
> + * It will be called by device driver core when no one holds a valid
> + * pointer to @dev anymore.
> + */
> +static void aie_part_release_device(struct device *dev)
> +{
> +       struct aie_partition *apart = dev_to_aiepart(dev);
> +       struct aie_device *adev = apart->adev;
> +       int ret;
> +
> +       ret = mutex_lock_interruptible(&adev->mlock);
> +       if (ret) {
> +               dev_warn(&apart->dev,
> +                        "getting adev->mlock is interrupted by signal\n");
> +       }
> +
> +       aie_resource_put_region(&adev->cols_res, apart->range.start.col,
> +                               apart->range.size.col);
> +       list_del(&apart->node);
> +       mutex_unlock(&adev->mlock);
> +       put_device(apart->dev.parent);
> +}
> +
> +/**
> + * aie_create_partition() - create AI engine partition instance
> + * @adev: AI engine device
> + * @range: AI engine partition range to check. A range describes a group
> + *        of AI engine tiles.
> + * @return: created AI engine partition pointer for success, and PTR_ERR
> + *         for failure.
> + *
> + * This function creates an AI engine partition instance.
> + * It creates AI engine partition, the AI engine partition device and
> + * the AI engine partition character device.
> + */
> +static struct aie_partition *aie_create_partition(struct aie_device *adev,
> +                                                 struct aie_range *range)
> +{
> +       struct aie_partition *apart;
> +       struct device *dev;
> +       char devname[32];
> +       int ret;
> +
> +       ret = mutex_lock_interruptible(&adev->mlock);
> +       if (ret)
> +               return ERR_PTR(ret);
> +
> +       ret = aie_resource_check_region(&adev->cols_res, range->start.col,
> +                                       range->size.col);
> +       if (ret != range->start.col) {
> +               dev_err(&adev->dev, "invalid partition (%u,%u)(%u,%u).\n",
> +                       range->start.col, range->start.row,
> +                       range->size.col, range->size.row);
> +               mutex_unlock(&adev->mlock);
> +               return ERR_PTR(-EINVAL);
> +       }
> +       ret = aie_resource_get_region(&adev->cols_res, range->start.col,
> +                                     range->size.col);
> +       if (ret != range->start.col) {
> +               dev_err(&adev->dev, "failed to get partition
> (%u,%u)(%u,%u).\n",
> +                       range->start.col, range->start.row,
> +                       range->size.col, range->size.row);
> +               mutex_unlock(&adev->mlock);
> +               return ERR_PTR(-EFAULT);
> +       }
> +       mutex_unlock(&adev->mlock);
> +
> +       apart = devm_kzalloc(&adev->dev, sizeof(*apart), GFP_KERNEL);
> +       if (!apart)
> +               return ERR_PTR(-ENOMEM);
> +
> +       apart->adev = adev;
> +       memcpy(&apart->range, range, sizeof(*range));
> +       mutex_init(&apart->mlock);
> +
> +       /* Create AI engine partition device */
> +       dev = &apart->dev;
> +       device_initialize(dev);
> +       dev->parent = &adev->dev;
> +       dev->class = aie_class;
> +       dev_set_drvdata(dev, apart);
> +       snprintf(devname, sizeof(devname) - 1, "aiepart_%d_%d",
> +                apart->range.start.col, apart->range.size.col);
> +       dev_set_name(dev, devname);
> +       /* We can now rely on the release function for cleanup */
> +       dev->release = aie_part_release_device;
> +       ret = device_add(dev);
> +       if (ret) {
> +               dev_err(dev, "device_add failed: %d\n", ret);
> +               put_device(dev);
> +               return ERR_PTR(ret);
> +       }
> +
> +       ret = mutex_lock_interruptible(&adev->mlock);
> +       if (ret) {
> +               put_device(dev);
> +               return ERR_PTR(ret);
> +       }
> +
> +       list_add_tail(&apart->node, &adev->partitions);
> +       mutex_unlock(&adev->mlock);
> +       get_device(&adev->dev);
> +       dev_dbg(dev, "created AIE partition device.\n");
> +
> +       return apart;
> +}
> +
> +struct aie_partition *
> +of_aie_part_probe(struct aie_device *adev, struct device_node *nc)
> +{
> +       struct aie_partition *apart;
> +       struct aie_range range;
> +       u32 partition_id, regs[4];
> +       int ret;
> +
> +       /* Select device driver */
> +       ret = of_property_read_u32_array(nc, "reg", regs, ARRAY_SIZE(regs));
> +       if (ret < 0) {
> +               dev_err(&adev->dev,
> +                       "probe %pOF failed, no tiles range information.\n",
> +                       nc);
> +               return ERR_PTR(ret);
> +       }
> +       range.start.col = regs[0];
> +       range.start.row = regs[1];
> +       range.size.col = regs[2];
> +       range.size.row = regs[3];
> +
> +       ret = of_property_read_u32_index(nc, "xlnx,partition-id", 0,
> +                                        &partition_id);
> +       if (ret < 0) {
> +               dev_err(&adev->dev,
> +                       "probe %pOF failed, no partition id.\n", nc);
> +               return ERR_PTR(ret);
> +       }
> +
> +       ret = mutex_lock_interruptible(&adev->mlock);
> +       if (ret)
> +               return ERR_PTR(ret);
> +
> +       apart = aie_get_partition_from_id(adev, partition_id);
> +       mutex_unlock(&adev->mlock);
> +       if (apart) {
> +               dev_err(&adev->dev,
> +                       "probe failed: partition %u exists.\n",
> +                       partition_id);
> +               return ERR_PTR(ret);
> +       }
> +
> +       apart = aie_create_partition(adev, &range);
> +       if (IS_ERR(apart)) {
> +               dev_err(&adev->dev,
> +                       "%s: failed to create part(%u,%u),(%u,%u).\n",
> +                       __func__, range.start.col, range.start.row,
> +                       range.size.col, range.size.row);
> +               return apart;
> +       }
> +
> +       of_node_get(nc);
> +       apart->dev.of_node = nc;
> +       apart->partition_id = partition_id;
> +
> +       dev_info(&adev->dev,
> +                "AI engine part(%u,%u),(%u,%u), id %u is probed
> successfully.\n",
> +                range.start.col, range.start.row,
> +                range.size.col, range.size.row, apart->partition_id);
> +
> +       return apart;
> +}
> +
> +/**
> + * aie_destroy_part() - destroy AI engine partition
> + * @apart: AI engine partition
> + *
> + * This function will remove AI engine partition.
> + */
> +void aie_part_remove(struct aie_partition *apart)
> +{
> +       device_del(&apart->dev);
> +       put_device(&apart->dev);
> +}
> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-res.c
> b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
> new file mode 100644
> index 0000000..36f08bf
> --- /dev/null
> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
> @@ -0,0 +1,114 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Xilinx AI Engine device driver
> + *
> + * Copyright (C) 2020 Xilinx, Inc.
> + */
> +
> +#include <linux/bitmap.h>
> +
> +#include "ai-engine-internal.h"
> +
> +/**
> + * aie_resource_initialize() - initialize AI engine resource
> + * @res: pointer to AI engine resource
> + * @count: total number of element of this resource
> + * @return: 0 for success, negative value for failure.
> + *
> + * This function will initialize the data structure for the
> + * resource.
> + */
> +int aie_resource_initialize(struct aie_resource *res, int count)
> +{
> +       if (!res || !count)
> +               return -EINVAL;
> +       res->bitmap = bitmap_zalloc(count, GFP_KERNEL);
> +       if (!res->bitmap)
> +               return -ENOMEM;
> +       res->total = count;
> +
> +       return 0;
> +}
> +
> +/**
> + * aie_resource_uninitialize() - uninitialize AI engine resource
> + * @res: pointer to AI engine resource
> + *
> + * This function will release the AI engine resource data members.
> + */
> +void aie_resource_uninitialize(struct aie_resource *res)
> +{
> +       res->total = 0;
> +       if (res->bitmap)
> +               bitmap_free(res->bitmap);
> +}
> +
> +/**
> + * aie_resource_check() - check availability of requested resource
> + * @res: pointer to AI engine resource to check
> + * @start: start index of the required resource, it will only be used if
> + *        @continuous is 1. It will check the available resource starting
> from
> + *        @start
> + * @count: number of requested element
> + * @return: start resource id if the requested number of resources are
> available
> + *         It will return negative value of errors.
> + *
> + * This function will check the availability. It will return start resource
> id
> + * if the requested number of resources are available.
> + */
> +int aie_resource_check_region(struct aie_resource *res,
> +                             u32 start, u32 count)
> +{
> +       unsigned long id;
> +
> +       if (!res || !res->bitmap || !count)
> +               return -EINVAL;
> +       id = bitmap_find_next_zero_area(res->bitmap, res->total, start,
> +                                       count, 0);
> +       if (id >= res->total)
> +               return -ERANGE;
> +
> +       return (int)id;
> +}
> +
> +/**
> + * aie_resource_get_region() - get requested AI engine resource
> + * @res: pointer to AI engine resource to check
> + * @count: number of requested element
> + * @start: start index of the required resource
> + * @return: start resource id for success, and negative value for failure.
> + *
> + * This function check if the requested AI engine resource is available.
> + * If it is available, mark it used and return the start resource id.
> + */
> +int aie_resource_get_region(struct aie_resource *res, u32 start, u32 count)
> +{
> +       unsigned long off;
> +
> +       if (!res || !res->bitmap || !count)
> +               return -EINVAL;
> +       off = bitmap_find_next_zero_area(res->bitmap, res->total, start,
> +                                        count, 0);
> +       if (off >= res->total) {
> +               pr_err("Failed to get available AI engine resource.\n");
> +               return -ERANGE;
> +       }
> +       bitmap_set(res->bitmap, off, count);
> +
> +       return (int)off;
> +}
> +
> +/**
> + * aie_resource_put_region() - release requested AI engine resource
> + * @res: pointer to AI engine resource to check
> + * @start: start index of the resource to release
> + * @count: number of elements to release
> + *
> + * This function release the requested AI engine resource.
> + */
> +void aie_resource_put_region(struct aie_resource *res, int start, u32 count)
> +{
> +       if (!res || !count)
> +               return;
> +       bitmap_clear(res->bitmap, start, count);
> +}
> diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-
> engine.h
> new file mode 100644
> index 0000000..acbc781
> --- /dev/null
> +++ b/include/uapi/linux/xlnx-ai-engine.h
> @@ -0,0 +1,107 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Copyright (c) 2020, Xilinx Inc.
> + */
> +
> +#ifndef _UAPI_AI_ENGINE_H_
> +#define _UAPI_AI_ENGINE_H_
> +
> +#include <linux/ioctl.h>
> +#include <linux/types.h>
> +
> +enum aie_reg_op {
> +       AIE_REG_WRITE,
> +};
> +
> +/* AI engine partition is in use */
> +#define XAIE_PART_STATUS_INUSE         (1U << 0)
> +
> +/**
> + * struct aie_location - AIE location information
> + * @col: column id
> + * @row: row id
> + */
> +struct aie_location {
> +       __u32 col;
> +       __u32 row;
> +};
> +
> +/**
> + * struct aie_range - AIE range information
> + * @start: start tile location
> + * @size: size of the range, number of columns and rows
> + */
> +struct aie_range {
> +       struct aie_location start;
> +       struct aie_location size;
> +};
> +
> +/**
> + * struct aie_reg_args - AIE access register arguments
> + * @op: if this request is to read, write or poll register
> + * @mask: mask for mask write, 0 for not mask write
> + * @offset: offset of register to the start of an AI engine partition
> + * @val: value to write or get
> + */
> +struct aie_reg_args {
> +       enum aie_reg_op op;
> +       __u32 mask;
> +       __u64 offset;
> +       __u32 val;
> +};
> +
> +/**
> + * struct aie_range_args - AIE range request arguments
> + * @partition_id: partition id. It is used to identify the
> + *               AI engine partition in the system.
> + * @uid: image identifier loaded on the AI engine partition
> + * @range: range of AIE tiles
> + * @status: indicate if the AI engine is in use.
> + *         0 means not in used, otherwise, in use.
> + */
> +struct aie_range_args {
> +       __u32 partition_id;
> +       __u32 uid;
> +       struct aie_range range;
> +       __u32 status;
> +};
> +
> +/**
> + * struct aie_partition_query - AIE partition query arguments
> + * @partition_cnt: number of defined partitions in the system
> + * @partitions: buffer to store defined partitions information.
> + */
> +struct aie_partition_query {
> +       struct aie_range_args *partitions;
> +       __u32 partition_cnt;
> +};
> +
> +/**
> + * struct aie_partition_req - AIE request partition arguments
> + * @partition_id: partition node id. It is used to identify the AI engine
> + *               partition in the system.
> + * @uid: image identifier loaded on the AI engine partition
> + * @meta_data: meta data to indicate which resources used by application.
> + * @flag: used for application to indicate particular driver requirements
> + *       application wants to have for the partition. e.g. do not clean
> + *       resource when closing the partition.
> + */
> +struct aie_partition_req {
> +       __u32 partition_id;
> +       __u32 uid;
> +       __u64 meta_data;
> +       __u32 flag;
> +};
> +
> +#define AIE_IOCTL_BASE 'A'
> +
> +/* AI engine device IOCTL operations */
> +#define AIE_ENQUIRE_PART_IOCTL         _IOWR(AIE_IOCTL_BASE, 0x1, \
> +                                             struct aie_partition_query)
> +#define AIE_REQUEST_PART_IOCTL         _IOR(AIE_IOCTL_BASE, 0x2, \
> +                                            struct aie_partition_req)
> +
> +/* AI engine partition IOCTL operations */
> +#define AIE_REG_IOCTL                  _IOWR(AIE_IOCTL_BASE, 0x8, \
> +                                             struct aie_reg_args)
> +#endif


2020-12-09 12:53:07

by Daniel Vetter

[permalink] [raw]
Subject: Re: [PATCH 2/9] misc: Add Xilinx AI engine device driver

On Tue, Dec 08, 2020 at 11:54:57AM -0800, Jiaying Liang wrote:
>
> On 12/8/20 9:12 AM, Nicolas Dufresne wrote:
> > Le mercredi 18 novembre 2020 ? 00:06 -0800, Wendy Liang a ?crit?:
> > > Create AI engine device/partition hierarchical structure.
> > >
> > > Each AI engine device can have multiple logical partitions(groups of AI
> > > engine tiles). Each partition is column based and has its own node ID
> > > in the system. AI engine device driver manages its partitions.
> > >
> > > Applications can access AI engine partition through the AI engine
> > > partition driver instance. AI engine registers write is moved to kernel
> > > as there are registers in the AI engine array needs privilege
> > > permission.
> > Hi there, it's nice to see an effort to upstream an AI driver. I'm a little
> > worried this driver is not obvious to use from it's source code itself. So you
> > have reference to some Open Source code that demonstrate it's usage ?
>
> We have AI engine library which provides a cross platforms APIs for other
>
> libraries/application to use the hardware. Here is the source code:
>
> https://github.com/Xilinx/embeddedsw/tree/master/XilinxProcessorIPLib/drivers/aienginev2/src
>
> The cross platforms AI engine library runs in LInux userspace it defines how
> to
>
> configure, and the kernel driver controls if what can be access and manage
> errors from device.

So I kinda ignored this driver submission because in the past all these AI
drivers had at best incomplete open source (usually the compiler is
closed, often also large parts of the runtime). I think yours would be the
first that breaks this trend, is that really the case? I.e. I could make
full use of this hw without any closed source bits to run DNN workloads
and things like that?

If that's the case then I think there's nothing stopping us from doing the
right thing and merging this driver into the right subsystem: The
subsystem for accelerators which their own memory and who want dma-buf
integration is drivers/gpu, not drivers/misc.

Apologies that I'm jumping with the really big arch review when v3 is
already on the list. But last few times merging AI drivers to drivers/misc
was really just a way to avoid the merge criteria for drivers/gpu
acceleration drivers. I'd love to land the first real open AI driver in
upstream, properly.

Cheers, Daniel



>
>
> Best Regards,
>
> Wendy
>
>
> >
> > > Signed-off-by: Wendy Liang <[email protected]>
> > > Signed-off-by: Hyun Kwon <[email protected]>
> > > ---
> > > ?MAINTAINERS??????????????????????????????????????? |?? 8 +
> > > ?drivers/misc/Kconfig?????????????????????????????? |? 12 +
> > > ?drivers/misc/Makefile????????????????????????????? |?? 1 +
> > > ?drivers/misc/xilinx-ai-engine/Makefile???????????? |? 11 +
> > > ?drivers/misc/xilinx-ai-engine/ai-engine-aie.c????? | 115 +++++
> > > ?drivers/misc/xilinx-ai-engine/ai-engine-dev.c????? | 448 ++++++++++++++++++
> > > ?drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 226 ++++++++++
> > > ?drivers/misc/xilinx-ai-engine/ai-engine-part.c???? | 498
> > > +++++++++++++++++++++
> > > ?drivers/misc/xilinx-ai-engine/ai-engine-res.c????? | 114 +++++
> > > ?include/uapi/linux/xlnx-ai-engine.h??????????????? | 107 +++++
> > > ?10 files changed, 1540 insertions(+)
> > > ?create mode 100644 drivers/misc/xilinx-ai-engine/Makefile
> > > ?create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-aie.c
> > > ?create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dev.c
> > > ?create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-internal.h
> > > ?create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-part.c
> > > ?create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-res.c
> > > ?create mode 100644 include/uapi/linux/xlnx-ai-engine.h
> > >
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 5cc595a..40e3351 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -19283,6 +19283,14 @@ T:?????git https://github.com/Xilinx/linux-xlnx.git
> > > ?F:?????Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml
> > > ?F:?????drivers/phy/xilinx/phy-zynqmp.c
> > > +XILINX AI ENGINE DRIVER
> > > +M:?????Wendy Liang <[email protected]>
> > > +S:?????Supported
> > > +F:?????Documentation/devicetree/bindings/soc/xilinx/xlnx,ai-engine.yaml
> > > +F:?????drivers/misc/xilinx-ai-engine/
> > > +F:?????include/linux/xlnx-ai-engine.h
> > > +F:?????include/uapi/linux/xlnx-ai-engine.h
> > > +
> > > ?XILLYBUS DRIVER
> > > ?M:?????Eli Billauer <[email protected]>
> > > ?L:[email protected]
> > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > > index fafa8b0..0b8ce4d 100644
> > > --- a/drivers/misc/Kconfig
> > > +++ b/drivers/misc/Kconfig
> > > @@ -444,6 +444,18 @@ config XILINX_SDFEC
> > > ????????? If unsure, say N.
> > > +config XILINX_AIE
> > > +???????tristate "Xilinx AI engine"
> > > +???????depends on ARM64 || COMPILE_TEST
> > > +???????help
> > > +???????? This option enables support for the Xilinx AI engine driver.
> > > +???????? One Xilinx AI engine device can have multiple partitions (groups of
> > > +???????? AI engine tiles). Xilinx AI engine device driver instance manages
> > > +???????? AI engine partitions. User application access its partitions through
> > > +???????? AI engine partition instance file operations.
> > > +
> > > +???????? If unsure, say N
> > > +
> > > ?config MISC_RTSX
> > > ????????tristate
> > > ????????default MISC_RTSX_PCI || MISC_RTSX_USB
> > > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> > > index d23231e..2176b18 100644
> > > --- a/drivers/misc/Makefile
> > > +++ b/drivers/misc/Makefile
> > > @@ -57,3 +57,4 @@ obj-$(CONFIG_HABANA_AI)???????????????+= habanalabs/
> > > ?obj-$(CONFIG_UACCE)????????????+= uacce/
> > > ?obj-$(CONFIG_XILINX_SDFEC)?????+= xilinx_sdfec.o
> > > ?obj-$(CONFIG_HISI_HIKEY_USB)???+= hisi_hikey_usb.o
> > > +obj-$(CONFIG_XILINX_AIE)???????+= xilinx-ai-engine/
> > > diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-
> > > engine/Makefile
> > > new file mode 100644
> > > index 0000000..7827a0a
> > > --- /dev/null
> > > +++ b/drivers/misc/xilinx-ai-engine/Makefile
> > > @@ -0,0 +1,11 @@
> > > +# SPDX-License-Identifier: GPL-2.0-only
> > > +#
> > > +# Makefile for Xilinx AI engine device driver
> > > +#
> > > +
> > > +obj-$(CONFIG_XILINX_AIE)???????+= xilinx-aie.o
> > > +
> > > +xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \
> > > +????????????????????????????????? ai-engine-dev.o \
> > > +????????????????????????????????? ai-engine-part.o \
> > > +????????????????????????????????? ai-engine-res.o
> > > diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
> > > b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
> > > new file mode 100644
> > > index 0000000..319260f
> > > --- /dev/null
> > > +++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
> > > @@ -0,0 +1,115 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Xilinx AI Engine driver AIE device specific implementation
> > > + *
> > > + * Copyright (C) 2020 Xilinx, Inc.
> > > + */
> > > +
> > > +#include <linux/slab.h>
> > > +
> > > +#include "ai-engine-internal.h"
> > > +
> > > +#define AIE_ARRAY_SHIFT????????????????30U
> > > +#define AIE_COL_SHIFT??????????23U
> > > +#define AIE_ROW_SHIFT??????????18U
> > > +
> > > +/*
> > > + * Registers offsets
> > > + */
> > > +#define AIE_SHIMNOC_L2INTR_MASK_REGOFF?????????0x00015000U
> > > +#define AIE_SHIMNOC_L2INTR_INTR_REGOFF?????????0x00015010U
> > > +#define AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF?????0x0001d000U
> > > +#define AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF?????0x0001d13cU
> > > +#define AIE_SHIMNOC_AXIMM_REGOFF???????????????0x0001e020U
> > > +#define AIE_SHIMPL_L1INTR_MASK_A_REGOFF????????????????0x00035000U
> > > +#define AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF?0x00035050U
> > > +#define AIE_SHIMPL_CLKCNTR_REGOFF??????????????0x00036040U
> > > +#define AIE_SHIMPL_RESET_REGOFF????????????????????????0x0003604cU
> > > +#define AIE_TILE_CORE_CLKCNTR_REGOFF???????????0x00036040U
> > > +
> > > +static const struct aie_tile_regs aie_kernel_regs[] = {
> > > +???????/* SHIM AXI MM Config */
> > > +???????{.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > +??????? .soff = AIE_SHIMNOC_AXIMM_REGOFF,
> > > +??????? .eoff = AIE_SHIMNOC_AXIMM_REGOFF,
> > > +???????},
> > > +???????/* SHIM DMA ADDRESS range */
> > > +???????{.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > +??????? .soff = AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF,
> > > +??????? .eoff = AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF,
> > > +???????},
> > > +???????/* SHIM 2nd level interrupt controller */
> > > +???????{.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > +??????? .soff = AIE_SHIMNOC_L2INTR_MASK_REGOFF,
> > > +??????? .eoff = AIE_SHIMNOC_L2INTR_INTR_REGOFF,
> > > +???????},
> > > +???????/* SHIM 1st level interrupt controller */
> > > +???????{.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
> > > +???????????????????? AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > +??????? .soff = AIE_SHIMPL_L1INTR_MASK_A_REGOFF,
> > > +??????? .eoff = AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF,
> > > +???????},
> > > +???????/* SHIM reset Enable */
> > > +???????{.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
> > > +???????????????????? AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > +??????? .soff = AIE_SHIMPL_RESET_REGOFF,
> > > +??????? .eoff = AIE_SHIMPL_RESET_REGOFF,
> > > +???????},
> > > +???????/* SHIM clock control */
> > > +???????{.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
> > > +???????????????????? AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > +??????? .soff = AIE_SHIMPL_CLKCNTR_REGOFF,
> > > +??????? .eoff = AIE_SHIMPL_CLKCNTR_REGOFF,
> > > +???????},
> > > +???????/* Tile clock control */
> > > +???????{.attribute = AIE_TILE_TYPE_TILE << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > +??????? .soff = AIE_TILE_CORE_CLKCNTR_REGOFF,
> > > +??????? .eoff = AIE_TILE_CORE_CLKCNTR_REGOFF,
> > > +???????},
> > > +};
> > > +
> > > +static u32 aie_get_tile_type(struct aie_location *loc)
> > > +{
> > > +???????if (loc->row)
> > > +???????????????return AIE_TILE_TYPE_TILE;
> > > +???????/* SHIM row */
> > > +???????if ((loc->col % 4) < 2)
> > > +???????????????return AIE_TILE_TYPE_SHIMPL;
> > > +
> > > +???????return AIE_TILE_TYPE_SHIMNOC;
> > > +}
> > > +
> > > +static const struct aie_tile_operations aie_ops = {
> > > +???????.get_tile_type = aie_get_tile_type,
> > > +};
> > > +
> > > +/**
> > > + * aie_device_init() - Initialize AI engine device struct AIE specific
> > > + *??????????????????? properties
> > > + * @adev: AI engine device
> > > + * @return: 0 for success, negative value for failure.
> > > + *
> > > + * This function initialize the AI engine device structure device version
> > > + * specific elements such as register addressing related array shift,
> > > + * column shift, and row shift; AIE specific device operations, device
> > > + * columns resource.
> > > + */
> > > +int aie_device_init(struct aie_device *adev)
> > > +{
> > > +???????int ret;
> > > +
> > > +???????adev->array_shift = AIE_ARRAY_SHIFT;
> > > +???????adev->col_shift = AIE_COL_SHIFT;
> > > +???????adev->row_shift = AIE_ROW_SHIFT;
> > > +???????adev->ops = &aie_ops;
> > > +???????adev->num_kernel_regs = ARRAY_SIZE(aie_kernel_regs);
> > > +???????adev->kernel_regs = aie_kernel_regs;
> > > +
> > > +???????/* Get the columns resource */
> > > +???????/* Get number of columns from AI engine memory resource */
> > > +???????ret = aie_resource_initialize(&adev->cols_res, 50);
> > > +???????if (ret)
> > > +???????????????dev_err(&adev->dev, "failed to initialize columns
> > > resource.\n");
> > > +
> > > +???????return ret;
> > > +}
> > > diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
> > > b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
> > > new file mode 100644
> > > index 0000000..2ab2dc8
> > > --- /dev/null
> > > +++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
> > > @@ -0,0 +1,448 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Xilinx AI Engine device driver
> > > + *
> > > + * Copyright (C) 2020 Xilinx, Inc.
> > > + */
> > > +
> > > +#include <linux/anon_inodes.h>
> > > +#include <linux/cdev.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/device.h>
> > > +#include <linux/dma-mapping.h>
> > > +#include <linux/file.h>
> > > +#include <linux/fs.h>
> > > +#include <linux/idr.h>
> > > +#include <linux/list.h>
> > > +#include <linux/module.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/of.h>
> > > +#include <linux/of_device.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/slab.h>
> > > +#include <linux/uaccess.h>
> > > +#include <uapi/linux/xlnx-ai-engine.h>
> > > +
> > > +#include "ai-engine-internal.h"
> > > +
> > > +#define AIE_DEV_MAX????(MINORMASK + 1)
> > > +
> > > +static dev_t aie_major;
> > > +struct class *aie_class;
> > > +
> > > +static DEFINE_IDA(aie_device_ida);
> > > +static DEFINE_IDA(aie_minor_ida);
> > > +
> > > +/**
> > > + * aie_get_partition_fd() - Get AI engine partition file descriptor
> > > + * @apart: AI engine partition
> > > + * @return: file descriptor for AI engine partition for success, or negative
> > > + *???????? value for failure.
> > > + *
> > > + * This function gets a file descriptor for the AI engine partition.
> > > + */
> > > +static int aie_get_partition_fd(struct aie_partition *apart)
> > > +{
> > > +???????struct file *filep;
> > > +???????int ret;
> > > +
> > > +???????/*
> > > +??????? * We can't use anon_inode_getfd() because we need to modify
> > > +??????? * the f_mode flags directly to allow more than just ioctls
> > > +??????? */
> > > +???????ret = get_unused_fd_flags(O_CLOEXEC);
> > > +???????if (ret < 0)
> > > +???????????????return ret;
> > > +
> > > +???????filep = anon_inode_getfile(dev_name(&apart->dev), &aie_part_fops,
> > > +????????????????????????????????? apart, O_RDWR);
> > > +???????if (IS_ERR(filep)) {
> > > +???????????????put_unused_fd(ret);
> > > +???????????????ret = PTR_ERR(filep);
> > > +???????????????return ret;
> > > +???????}
> > > +???????filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
> > > +???????fd_install(ret, filep);
> > > +
> > > +???????return ret;
> > > +}
> > > +
> > > +/**
> > > + * aie_enquire_partitions() - get AI engine partitions information
> > > + * @adev: AI engine device
> > > + * @query: data struct to store the partition information
> > > + * @return: 0 for success, and negative value for failure.
> > > + */
> > > +static int aie_enquire_partitions(struct aie_device *adev,
> > > +???????????????????????????????? struct aie_partition_query *query)
> > > +{
> > > +???????struct aie_partition *apart;
> > > +???????u32 partition_cnt, i = 0;
> > > +???????int ret;
> > > +
> > > +???????if (!query->partitions) {
> > > +???????????????/*
> > > +??????????????? * If partitions information buffer is NULL.
> > > +??????????????? * It is to get the number of partitions.
> > > +??????????????? */
> > > +???????????????query->partition_cnt = 0;
> > > +???????????????list_for_each_entry(apart, &adev->partitions, node)
> > > +???????????????????????query->partition_cnt++;
> > > +???????????????return 0;
> > > +???????}
> > > +
> > > +???????partition_cnt = query->partition_cnt;
> > > +???????if (!partition_cnt)
> > > +???????????????return 0;
> > > +
> > > +???????ret = mutex_lock_interruptible(&adev->mlock);
> > > +???????if (ret)
> > > +???????????????return ret;
> > > +
> > > +???????list_for_each_entry(apart, &adev->partitions, node) {
> > > +???????????????struct aie_range_args part;
> > > +
> > > +???????????????if (i >= partition_cnt)
> > > +???????????????????????break;
> > > +???????????????part.partition_id = apart->partition_id;
> > > +???????????????/*
> > > +??????????????? * TBD: check with PLM that if the partition is programmed
> > > +??????????????? * and get the UID of the image which is loaded on the AI
> > > +??????????????? * engine partition.
> > > +??????????????? */
> > > +???????????????part.uid = 0;
> > > +???????????????part.range.start.col = apart->range.start.col;
> > > +???????????????part.range.start.row = apart->range.start.row;
> > > +???????????????part.range.size.col = apart->range.size.col;
> > > +???????????????part.range.size.row = apart->range.size.row;
> > > +???????????????/* Check if partition is in use */
> > > +???????????????part.status = apart->status;
> > > +???????????????if (copy_to_user((void __user *)&query->partitions[i], &part,
> > > +??????????????????????????????? sizeof(part))) {
> > > +???????????????????????mutex_unlock(&adev->mlock);
> > > +???????????????????????return -EFAULT;
> > > +???????????????}
> > > +???????????????i++;
> > > +???????}
> > > +???????mutex_unlock(&adev->mlock);
> > > +???????query->partition_cnt = i;
> > > +
> > > +???????return 0;
> > > +}
> > > +
> > > +/**
> > > + * aie_get_partition_from_id() - get AI engine partition from id
> > > + * @adev: AI engine device
> > > + * @partition_id: partition id to check
> > > + * @return: partition pointer if partition exists, otherwise, NULL.
> > > + *
> > > + * This function checks defined partitions with partition id.
> > > + * This function expect the caller to lock mlock of @adev.
> > > + */
> > > +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
> > > +???????????????????????????????????????????????u32 partition_id)
> > > +{
> > > +???????struct aie_partition *apart;
> > > +
> > > +???????list_for_each_entry(apart, &adev->partitions, node) {
> > > +???????????????if (apart->partition_id == partition_id)
> > > +???????????????????????return apart;
> > > +???????}
> > > +
> > > +???????return NULL;
> > > +}
> > > +
> > > +/**
> > > + * aie_request_partition() - request AI engine partition
> > > + * @adev: AI engine device
> > > + * @req: partition request, includes the requested AI engine information
> > > + *????? such as partition node ID and the UID of the image which is
> > > + *????? loaded on the partition.
> > > + * @return: partition pointer if partition exists, otherwise, NULL.
> > > + *
> > > + * This function finds a defined partition which matches the specified
> > > + * partition id, request it if it hasn't been requested, and returns it.
> > > + */
> > > +struct aie_partition *aie_request_partition(struct aie_device *adev,
> > > +?????????????????????????????????????????? struct aie_partition_req *req)
> > > +{
> > > +???????struct aie_partition *apart;
> > > +???????int ret;
> > > +
> > > +???????ret = mutex_lock_interruptible(&adev->mlock);
> > > +???????if (ret)
> > > +???????????????return ERR_PTR(ret);
> > > +
> > > +???????apart = aie_get_partition_from_id(adev, req->partition_id);
> > > +???????if (!apart) {
> > > +???????????????dev_err(&adev->dev,
> > > +???????????????????????"request partition %u failed, not exist.\n",
> > > +???????????????????????req->partition_id);
> > > +???????????????mutex_unlock(&adev->mlock);
> > > +???????????????return ERR_PTR(-EINVAL);
> > > +???????}
> > > +???????/*
> > > +??????? * TBD: It will check image UID too to see if the user matches
> > > +??????? * what's loaded in the AI engine partition. And check the meta
> > > +??????? * data to see which resources used by application.
> > > +??????? */
> > > +
> > > +???????ret = mutex_lock_interruptible(&apart->mlock);
> > > +???????if (ret)
> > > +???????????????return ERR_PTR(ret);
> > > +
> > > +???????if (apart->status & XAIE_PART_STATUS_INUSE) {
> > > +???????????????mutex_unlock(&apart->mlock);
> > > +???????????????dev_err(&adev->dev,
> > > +???????????????????????"request partition %u failed, partition in use.\n",
> > > +???????????????????????req->partition_id);
> > > +???????????????apart = ERR_PTR(-EBUSY);
> > > +???????} else {
> > > +???????????????/*
> > > +??????????????? * TBD:
> > > +??????????????? * 1. setup NOC AXI MM config to only generate error events
> > > +??????????????? *??? for slave error and decode error.
> > > +??????????????? * 2. scan to see which tiles have been clock gated.
> > > +??????????????? *
> > > +??????????????? * This needs to be done before the AI engine partition is
> > > +??????????????? * exported for user to access.
> > > +??????????????? */
> > > +???????????????apart->status = XAIE_PART_STATUS_INUSE;
> > > +???????????????mutex_unlock(&apart->mlock);
> > > +???????}
> > > +???????mutex_unlock(&adev->mlock);
> > > +
> > > +???????return apart;
> > > +}
> > > +
> > > +static long xilinx_ai_engine_ioctl(struct file *filp, unsigned int cmd,
> > > +????????????????????????????????? unsigned long arg)
> > > +{
> > > +???????struct inode *inode = file_inode(filp);
> > > +???????struct aie_device *adev = cdev_to_aiedev(inode->i_cdev);
> > > +???????void __user *argp = (void __user *)arg;
> > > +???????int ret;
> > > +
> > > +???????switch (cmd) {
> > > +???????case AIE_ENQUIRE_PART_IOCTL:
> > > +???????{
> > > +???????????????struct aie_partition_query query;
> > > +???????????????struct aie_partition_query? __user *uquery_ptr = argp;
> > > +
> > > +???????????????if (copy_from_user(&query, uquery_ptr, sizeof(query)))
> > > +???????????????????????return -EFAULT;
> > > +???????????????ret = aie_enquire_partitions(adev, &query);
> > > +???????????????if (ret < 0)
> > > +???????????????????????return ret;
> > > +???????????????if (copy_to_user((void __user *)&uquery_ptr->partition_cnt,
> > > +??????????????????????????????? &query.partition_cnt,
> > > +??????????????????????????????? sizeof(query.partition_cnt)))
> > > +???????????????????????return -EFAULT;
> > > +???????????????break;
> > > +???????}
> > > +???????case AIE_REQUEST_PART_IOCTL:
> > > +???????{
> > > +???????????????struct aie_partition_req req;
> > > +???????????????struct aie_partition *apart;
> > > +
> > > +???????????????if (copy_from_user(&req, argp, sizeof(req)))
> > > +???????????????????????return -EFAULT;
> > > +???????????????apart = aie_request_partition(adev, &req);
> > > +???????????????if (IS_ERR(apart))
> > > +???????????????????????return PTR_ERR(apart);
> > > +???????????????ret = aie_get_partition_fd(apart);
> > > +???????????????if (ret < 0) {
> > > +???????????????????????dev_err(&apart->dev, "failed to get fd.\n");
> > > +???????????????????????break;
> > > +???????????????}
> > > +???????????????break;
> > > +???????}
> > > +???????default:
> > > +???????????????dev_err(&adev->dev, "Invalid ioctl command %u.\n", cmd);
> > > +???????????????ret = -EINVAL;
> > > +???????????????break;
> > > +???????}
> > > +
> > > +???????return ret;
> > > +}
> > > +
> > > +static const struct file_operations aie_device_fops = {
> > > +???????.owner??????????= THIS_MODULE,
> > > +???????.unlocked_ioctl?= xilinx_ai_engine_ioctl,
> > > +};
> > > +
> > > +static void xilinx_ai_engine_release_device(struct device *dev)
> > > +{
> > > +???????struct aie_device *adev = dev_to_aiedev(dev);
> > > +
> > > +???????ida_simple_remove(&aie_device_ida, dev->id);
> > > +???????ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
> > > +???????cdev_del(&adev->cdev);
> > > +???????aie_resource_uninitialize(&adev->cols_res);
> > > +}
> > > +
> > > +/**
> > > + * of_xilinx_ai_engine_part_probe() - probes for AI engine partition nodes
> > > + * @adev: AI engine device
> > > + *
> > > + * This function will probe for children AI engine partition nodes and create
> > > + * an AI engine partition instance for each node.
> > > + */
> > > +static void of_xilinx_ai_engine_part_probe(struct aie_device *adev)
> > > +{
> > > +???????struct device_node *nc;
> > > +
> > > +???????for_each_available_child_of_node(adev->dev.of_node, nc) {
> > > +???????????????struct aie_partition *apart;
> > > +
> > > +???????????????if (of_node_test_and_set_flag(nc, OF_POPULATED))
> > > +???????????????????????continue;
> > > +???????????????apart = of_aie_part_probe(adev, nc);
> > > +???????????????if (IS_ERR(apart)) {
> > > +???????????????????????dev_err(&adev->dev,
> > > +???????????????????????????????"Failed to probe AI engine part for %pOF\n",
> > > +???????????????????????????????nc);
> > > +???????????????????????of_node_clear_flag(nc, OF_POPULATED);
> > > +???????????????}
> > > +???????}
> > > +}
> > > +
> > > +static int xilinx_ai_engine_probe(struct platform_device *pdev)
> > > +{
> > > +???????struct aie_device *adev;
> > > +???????struct device *dev;
> > > +???????int ret;
> > > +
> > > +???????adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
> > > +???????if (!adev)
> > > +???????????????return -ENOMEM;
> > > +???????platform_set_drvdata(pdev, adev);
> > > +???????INIT_LIST_HEAD(&adev->partitions);
> > > +???????mutex_init(&adev->mlock);
> > > +
> > > +???????adev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > > +???????if (!adev->res) {
> > > +???????????????dev_err(&pdev->dev, "No memory resource.\n");
> > > +???????????????return -EINVAL;
> > > +???????}
> > > +???????adev->base = devm_ioremap_resource(&pdev->dev, adev->res);
> > > +???????if (IS_ERR(adev->base)) {
> > > +???????????????dev_err(&pdev->dev, "no io memory resource.\n");
> > > +???????????????return PTR_ERR(adev->base);
> > > +???????}
> > > +
> > > +???????/* Initialize AIE device specific instance. */
> > > +???????ret = aie_device_init(adev);
> > > +???????if (ret < 0) {
> > > +???????????????dev_err(&pdev->dev, "failed to initialize device
> > > instance.\n");
> > > +???????????????return ret;
> > > +???????}
> > > +
> > > +???????dev = &adev->dev;
> > > +???????device_initialize(dev);
> > > +???????dev->class = aie_class;
> > > +???????dev->parent = &pdev->dev;
> > > +???????dev->of_node = pdev->dev.of_node;
> > > +
> > > +???????ret = ida_simple_get(&aie_minor_ida, 0, AIE_DEV_MAX, GFP_KERNEL);
> > > +???????if (ret < 0)
> > > +???????????????goto free_dev;
> > > +???????dev->devt = MKDEV(MAJOR(aie_major), ret);
> > > +???????ret = ida_simple_get(&aie_device_ida, 0, 0, GFP_KERNEL);
> > > +???????if (ret < 0)
> > > +???????????????goto free_minor_ida;
> > > +???????dev->id = ret;
> > > +???????dev_set_name(&adev->dev, "aie%d", dev->id);
> > > +
> > > +???????cdev_init(&adev->cdev, &aie_device_fops);
> > > +???????adev->cdev.owner = THIS_MODULE;
> > > +???????ret = cdev_add(&adev->cdev, dev->devt, 1);
> > > +???????if (ret)
> > > +???????????????goto free_ida;
> > > +???????/* We can now rely on the release function for cleanup */
> > > +???????dev->release = xilinx_ai_engine_release_device;
> > > +
> > > +???????ret = device_add(dev);
> > > +???????if (ret) {
> > > +???????????????dev_err(&pdev->dev, "device_add failed: %d\n", ret);
> > > +???????????????put_device(dev);
> > > +???????????????return ret;
> > > +???????}
> > > +
> > > +???????of_xilinx_ai_engine_part_probe(adev);
> > > +???????dev_info(&pdev->dev, "Xilinx AI Engine device(cols=%u) probed\n",
> > > +??????????????? adev->cols_res.total);
> > > +???????return 0;
> > > +
> > > +free_ida:
> > > +???????ida_simple_remove(&aie_device_ida, dev->id);
> > > +free_minor_ida:
> > > +???????ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
> > > +free_dev:
> > > +???????put_device(dev);
> > > +
> > > +???????return ret;
> > > +}
> > > +
> > > +static int xilinx_ai_engine_remove(struct platform_device *pdev)
> > > +{
> > > +???????struct aie_device *adev = platform_get_drvdata(pdev);
> > > +???????struct aie_partition *apart;
> > > +
> > > +???????list_for_each_entry(apart, &adev->partitions, node)
> > > +???????????????aie_part_remove(apart);
> > > +
> > > +???????device_del(&adev->dev);
> > > +???????put_device(&adev->dev);
> > > +
> > > +???????return 0;
> > > +}
> > > +
> > > +static const struct of_device_id xilinx_ai_engine_of_match[] = {
> > > +???????{ .compatible = "xlnx,ai-engine-v1.0", },
> > > +???????{ /* end of table */ },
> > > +};
> > > +MODULE_DEVICE_TABLE(of, xilinx_ai_engine_of_match);
> > > +
> > > +static struct platform_driver xilinx_ai_engine_driver = {
> > > +???????.probe??????????????????= xilinx_ai_engine_probe,
> > > +???????.remove?????????????????= xilinx_ai_engine_remove,
> > > +???????.driver?????????????????= {
> > > +???????????????.name???????????= "xilinx-ai-engine",
> > > +???????????????.of_match_table?= xilinx_ai_engine_of_match,
> > > +???????},
> > > +};
> > > +
> > > +static int __init xilinx_ai_engine_init(void)
> > > +{
> > > +???????int ret;
> > > +
> > > +???????ret = alloc_chrdev_region(&aie_major, 0, AIE_DEV_MAX, "aie");
> > > +???????if (ret < 0) {
> > > +???????????????pr_err("aie: failed to allocate aie region\n");
> > > +???????????????return ret;
> > > +???????}
> > > +
> > > +???????aie_class = class_create(THIS_MODULE, "aie");
> > > +???????if (IS_ERR(aie_class)) {
> > > +???????????????pr_err("failed to create aie class\n");
> > > +???????????????unregister_chrdev_region(aie_major, AIE_DEV_MAX);
> > > +???????????????return PTR_ERR(aie_class);
> > > +???????}
> > > +
> > > +???????platform_driver_register(&xilinx_ai_engine_driver);
> > > +
> > > +???????return 0;
> > > +}
> > > +postcore_initcall(xilinx_ai_engine_init);
> > > +
> > > +static void __exit xilinx_ai_engine_exit(void)
> > > +{
> > > +???????platform_driver_unregister(&xilinx_ai_engine_driver);
> > > +???????class_destroy(aie_class);
> > > +???????unregister_chrdev_region(aie_major, AIE_DEV_MAX);
> > > +}
> > > +module_exit(xilinx_ai_engine_exit);
> > > +
> > > +MODULE_AUTHOR("Xilinx, Inc.");
> > > +MODULE_LICENSE("GPL v2");
> > > diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
> > > b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
> > > new file mode 100644
> > > index 0000000..6a69946
> > > --- /dev/null
> > > +++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
> > > @@ -0,0 +1,226 @@
> > > +/* SPDX-License-Identifier: GPL-2.0-only */
> > > +/*
> > > + * Xilinx AI Engine driver internal header
> > > + *
> > > + * Copyright (C) 2020 Xilinx, Inc.
> > > + */
> > > +
> > > +#ifndef AIE_INTERNAL_H
> > > +#define AIE_INTERNAL_H
> > > +
> > > +#include <linux/bitfield.h>
> > > +#include <linux/bits.h>
> > > +#include <linux/cdev.h>
> > > +#include <linux/device.h>
> > > +#include <linux/io.h>
> > > +#include <linux/list.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/of.h>
> > > +#include <linux/of_device.h>
> > > +#include <uapi/linux/xlnx-ai-engine.h>
> > > +
> > > +/*
> > > + * Macros for AI engine tile type bitmasks
> > > + */
> > > +#define AIE_TILE_TYPE_TILE?????BIT(0)
> > > +#define AIE_TILE_TYPE_SHIMPL???BIT(1)
> > > +/* SHIM NOC tile includes SHIM PL and SHIM NOC modules */
> > > +#define AIE_TILE_TYPE_SHIMNOC??BIT(2)
> > > +
> > > +/*
> > > + * Macros for attribute property of AI engine registers accessed by kernel
> > > + * 0 - 7 bits: tile type bits
> > > + * 8 - 15 bits: permission bits. If it is 1, it allows write from userspace
> > > + */
> > > +#define AIE_REGS_ATTR_TILE_TYPE_SHIFT??0U
> > > +#define AIE_REGS_ATTR_PERM_SHIFT???????8U
> > > +#define AIE_REGS_ATTR_TILE_TYPE_MASK???GENMASK(AIE_REGS_ATTR_PERM_SHIFT - 1,
> > > \
> > > +???????????????????????????????????????????????AIE_REGS_ATTR_TILE_TYPE_SHIFT)
> > > +#define AIE_REGS_ATTR_PERM_MASK????????????????GENMASK(15, \
> > > +???????????????????????????????????????????????AIE_REGS_ATTR_PERM_SHIFT)
> > > +
> > > +/**
> > > + * struct aie_tile_regs - contiguous range of AI engine register
> > > + *?????????????????????? within an AI engine tile
> > > + * @soff: start offset of the range
> > > + * @eoff: end offset of the range
> > > + * @attribute: registers attribute. It uses AIE_REGS_ATTR_* macros defined
> > > + *??????????? above.
> > > + */
> > > +struct aie_tile_regs {
> > > +???????size_t soff;
> > > +???????size_t eoff;
> > > +???????u32 attribute;
> > > +};
> > > +
> > > +struct aie_device;
> > > +struct aie_partition;
> > > +
> > > +/**
> > > + * struct aie_tile_operations - AI engine device operations
> > > + * @get_tile_type: get type of tile based on tile operation
> > > + *
> > > + * Different AI engine device version has its own device
> > > + * operation.
> > > + */
> > > +struct aie_tile_operations {
> > > +???????u32 (*get_tile_type)(struct aie_location *loc);
> > > +};
> > > +
> > > +/**
> > > + * struct aie_resource - AI engine resource structure
> > > + * @bitmap: resource bitmap
> > > + * @total: total number of resource
> > > + */
> > > +struct aie_resource {
> > > +???????unsigned long *bitmap;
> > > +???????u32 total;
> > > +};
> > > +
> > > +/**
> > > + * struct aie_device - AI engine device structure
> > > + * @partitions: list of partitions requested
> > > + * @cdev: cdev for the AI engine
> > > + * @dev: device for the AI engine device
> > > + * @mlock: protection for AI engine device operations
> > > + * @base: AI engine device base virtual address
> > > + * @res: memory resource of AI engine device
> > > + * @kernel_regs: array of kernel only registers
> > > + * @ops: tile operations
> > > + * @size: size of the AI engine address space
> > > + * @array_shift: array address shift
> > > + * @col_shift: column address shift
> > > + * @row_shift: row address shift
> > > + * @cols_res: AI engine columns resources to indicate
> > > + *?????????? while columns are occupied by partitions.
> > > + * @num_kernel_regs: number of kernel only registers range
> > > + * @version: AI engine device version
> > > + */
> > > +struct aie_device {
> > > +???????struct list_head partitions;
> > > +???????struct cdev cdev;
> > > +???????struct device dev;
> > > +???????struct mutex mlock; /* protection for AI engine partitions */
> > > +???????void __iomem *base;
> > > +???????struct resource *res;
> > > +???????const struct aie_tile_regs *kernel_regs;
> > > +???????const struct aie_tile_operations *ops;
> > > +???????size_t size;
> > > +???????struct aie_resource cols_res;
> > > +???????u32 array_shift;
> > > +???????u32 col_shift;
> > > +???????u32 row_shift;
> > > +???????u32 num_kernel_regs;
> > > +???????int version;
> > > +};
> > > +
> > > +/**
> > > + * struct aie_partition - AI engine partition structure
> > > + * @node: list node
> > > + * @adev: pointer to AI device instance
> > > + * @range: range of partition
> > > + * @mlock: protection for AI engine partition operations
> > > + * @dev: device for the AI engine partition
> > > + * @partition_id: partition id. Partition ID is the identifier
> > > + *?????????????? of the AI engine partition in the system.
> > > + * @status: indicate if the partition is in use
> > > + */
> > > +struct aie_partition {
> > > +???????struct list_head node;
> > > +???????struct aie_device *adev;
> > > +???????struct aie_range range;
> > > +???????struct mutex mlock; /* protection for AI engine partition operations
> > > */
> > > +???????struct device dev;
> > > +???????u32 partition_id;
> > > +???????u32 status;
> > > +};
> > > +
> > > +extern struct class *aie_class;
> > > +extern const struct file_operations aie_part_fops;
> > > +
> > > +#define cdev_to_aiedev(i_cdev) container_of((i_cdev), struct aie_device,
> > > cdev)
> > > +#define dev_to_aiedev(_dev) container_of((_dev), struct aie_device, dev)
> > > +#define dev_to_aiepart(_dev) container_of((_dev), struct aie_partition, dev)
> > > +
> > > +#define aie_col_mask(adev) ({ \
> > > +???????struct aie_device *_adev = (adev); \
> > > +???????GENMASK_ULL(_adev->array_shift - 1, _adev->col_shift);? \
> > > +???????})
> > > +
> > > +#define aie_row_mask(adev) ({ \
> > > +???????struct aie_device *_adev = (adev); \
> > > +???????GENMASK_ULL(_adev->col_shift - 1, _adev->row_shift);? \
> > > +???????})
> > > +
> > > +#define aie_tile_reg_mask(adev) ({ \
> > > +???????struct aie_device *_adev = (adev); \
> > > +???????GENMASK_ULL(_adev->row_shift - 1, 0);? \
> > > +???????})
> > > +
> > > +/*
> > > + * Need to define field get, as AI engine shift mask is not constant.
> > > + * Cannot use FIELD_GET()
> > > + */
> > > +#define aie_tile_reg_field_get(mask, shift, regoff) ( \
> > > +???????((regoff) & (mask)) >> (shift))
> > > +
> > > +#define aie_cal_tile_reg(adev, regoff) ( \
> > > +???????aie_tile_reg_field_get(aie_tile_reg_mask(adev), 0, regoff))
> > > +
> > > +/**
> > > + * aie_cal_regoff() - calculate register offset to the whole AI engine
> > > + *?????????????????? device start address
> > > + * @adev: AI engine device
> > > + * @loc: AI engine tile location
> > > + * @regoff_intile: register offset within a tile
> > > + * @return: register offset to the whole AI engine device start address
> > > + */
> > > +static inline u32 aie_cal_regoff(struct aie_device *adev,
> > > +??????????????????????????????? struct aie_location loc, u32 regoff_intile)
> > > +{
> > > +???????return regoff_intile + (loc.col << adev->col_shift) +
> > > +????????????? (loc.row << adev->row_shift);
> > > +}
> > > +
> > > +/**
> > > + * aie_validate_location() - validate tile location within an AI engine
> > > + *????????????????????????? partition
> > > + * @apart: AI engine partition
> > > + * @loc: AI engine tile location
> > > + * @return: return 0 if it is valid, negative value for errors.
> > > + *
> > > + * This function checks if the AI engine location is within the AI engine
> > > + * partition.
> > > + */
> > > +static inline int aie_validate_location(struct aie_partition *apart,
> > > +???????????????????????????????????????struct aie_location loc)
> > > +{
> > > +???????if (loc.col < apart->range.start.col ||
> > > +?????????? loc.col >= apart->range.start.col + apart->range.size.col ||
> > > +?????????? loc.row < apart->range.start.row ||
> > > +?????????? loc.row >= apart->range.start.row + apart->range.size.row)
> > > +???????????????return -EINVAL;
> > > +
> > > +???????return 0;
> > > +}
> > > +
> > > +int aie_resource_initialize(struct aie_resource *res, int count);
> > > +void aie_resource_uninitialize(struct aie_resource *res);
> > > +int aie_resource_check_region(struct aie_resource *res, u32 start,
> > > +???????????????????????????? u32 count);
> > > +int aie_resource_get_region(struct aie_resource *res, u32 start,
> > > +?????????????????????????? u32 count);
> > > +void aie_resource_put_region(struct aie_resource *res, int start, u32 count);
> > > +
> > > +const struct file_operations *aie_part_get_fops(void);
> > > +u8 aie_part_in_use(struct aie_partition *apart);
> > > +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
> > > +???????????????????????????????????????????????u32 partition_id);
> > > +struct aie_partition *aie_request_partition(struct aie_device *adev,
> > > +?????????????????????????????????????????? struct aie_partition_req *req);
> > > +struct aie_partition *of_aie_part_probe(struct aie_device *adev,
> > > +???????????????????????????????????????struct device_node *nc);
> > > +void aie_part_remove(struct aie_partition *apart);
> > > +
> > > +int aie_device_init(struct aie_device *adev);
> > > +#endif /* AIE_INTERNAL_H */
> > > diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c
> > > b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
> > > new file mode 100644
> > > index 0000000..fc8f9f5
> > > --- /dev/null
> > > +++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
> > > @@ -0,0 +1,498 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Xilinx AI Engine partition driver
> > > + *
> > > + * Copyright (C) 2020 Xilinx, Inc.
> > > + */
> > > +
> > > +#include <linux/cdev.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/device.h>
> > > +#include <linux/fs.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/list.h>
> > > +#include <linux/mm.h>
> > > +#include <linux/mman.h>
> > > +#include <linux/mmu_context.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/of.h>
> > > +#include <linux/of_device.h>
> > > +#include <linux/slab.h>
> > > +#include <linux/uaccess.h>
> > > +#include <linux/uio.h>
> > > +#include <uapi/linux/xlnx-ai-engine.h>
> > > +
> > > +#include "ai-engine-internal.h"
> > > +
> > > +/**
> > > + * aie_cal_loc() - calculate tile location from register offset to the AI
> > > + *??????????????? engine device
> > > + * @adev: AI engine device
> > > + * @loc: memory pointer to restore returning location information
> > > + * @regoff: tile internal register offset
> > > + *
> > > + * This function returns the tile location.
> > > + */
> > > +static void aie_cal_loc(struct aie_device *adev,
> > > +???????????????????????struct aie_location *loc, u64 regoff)
> > > +{
> > > +???????loc->col = (u32)aie_tile_reg_field_get(aie_col_mask(adev),
> > > +????????????????????????????????????????????? adev->col_shift, regoff);
> > > +???????loc->row = (u32)aie_tile_reg_field_get(aie_row_mask(adev),
> > > +????????????????????????????????????????????? adev->row_shift, regoff);
> > > +}
> > > +
> > > +/**
> > > + * aie_part_reg_validation() - validate AI engine partition register access
> > > + * @apart: AI engine partition
> > > + * @offset: AI engine register offset
> > > + * @len: len of data to write/read
> > > + * @is_write: is the access to write to register
> > > + * @return: 0 for success, or negative value for failure.
> > > + *
> > > + * This function validate if the register to access is within the AI engine
> > > + * partition. If it is write access, if the register is writable by user.
> > > + */
> > > +static int aie_part_reg_validation(struct aie_partition *apart, size_t
> > > offset,
> > > +????????????????????????????????? size_t len, u8 is_write)
> > > +{
> > > +???????struct aie_device *adev;
> > > +???????u32 regend32, ttype;
> > > +???????u64 regoff, regend64;
> > > +???????struct aie_location loc;
> > > +???????unsigned int i;
> > > +
> > > +???????adev = apart->adev;
> > > +???????if (offset % sizeof(u32)) {
> > > +???????????????dev_err(&apart->dev,
> > > +???????????????????????"Invalid reg off(0x%zx), not 32bit aligned.\n",
> > > +???????????????????????offset);
> > > +???????????????return -EINVAL;
> > > +???????}
> > > +
> > > +???????if (len % sizeof(u32)) {
> > > +???????????????dev_err(&apart->dev, "Invalid reg operation len %zu.\n", len);
> > > +???????????????return -EINVAL;
> > > +???????}
> > > +
> > > +???????regoff = aie_cal_tile_reg(adev, offset);
> > > +???????regend64 = regoff + len;
> > > +???????if (regend64 >= BIT_ULL(adev->row_shift)) {
> > > +???????????????dev_err(&apart->dev,
> > > +???????????????????????"Invalid reg operation len %zu.\n", len);
> > > +???????????????return -EINVAL;
> > > +???????}
> > > +
> > > +???????aie_cal_loc(adev, &loc, offset);
> > > +???????if (aie_validate_location(apart, loc)) {
> > > +???????????????dev_err(&apart->dev,
> > > +???????????????????????"Invalid (%d,%d) out of part(%d,%d),(%d,%d)\n",
> > > +???????????????????????loc.col, loc.row,
> > > +???????????????????????apart->range.start.col, apart->range.start.row,
> > > +???????????????????????apart->range.size.col, apart->range.size.row);
> > > +???????????????return -EINVAL;
> > > +???????}
> > > +
> > > +???????if (!is_write)
> > > +???????????????return 0;
> > > +
> > > +???????regend32 = lower_32_bits(regend64);
> > > +???????ttype = adev->ops->get_tile_type(&loc);
> > > +???????for (i = 0; i < adev->num_kernel_regs; i++) {
> > > +???????????????const struct aie_tile_regs *regs;
> > > +???????????????u32 rttype, writable;
> > > +
> > > +???????????????regs = &adev->kernel_regs[i];
> > > +???????????????rttype = (regs->attribute & AIE_REGS_ATTR_TILE_TYPE_MASK) >>
> > > +??????????????????????? AIE_REGS_ATTR_TILE_TYPE_SHIFT;
> > > +???????????????writable = (regs->attribute & AIE_REGS_ATTR_PERM_MASK) >>
> > > +????????????????????????? AIE_REGS_ATTR_PERM_SHIFT;
> > > +???????????????if (!(ttype & rttype))
> > > +???????????????????????continue;
> > > +???????????????if ((regoff >= regs->soff && regoff <= regs->eoff) ||
> > > +?????????????????? (regend32 >= regs->soff && regend32 <= regs->eoff)) {
> > > +???????????????????????if (!writable) {
> > > +???????????????????????????????dev_err(&apart->dev,
> > > +???????????????????????????????????????"reg 0x%zx,0x%zx not writable.\n",
> > > +???????????????????????????????????????offset, len);
> > > +???????????????????????????????return -EINVAL;
> > > +???????????????????????}
> > > +???????????????}
> > > +???????}
> > > +
> > > +???????return 0;
> > > +}
> > > +
> > > +/**
> > > + * aie_part_write_register() - AI engine partition write register
> > > + * @apart: AI engine partition
> > > + * @offset: AI engine register offset
> > > + * @len: len of data to write
> > > + * @data: data to write
> > > + * @mask: mask, if it is non 0, it is mask write.
> > > + * @return: number of bytes write for success, or negative value for failure.
> > > + *
> > > + * This function writes data to the specified registers.
> > > + * If the mask is non 0, it is mask write.
> > > + */
> > > +static int aie_part_write_register(struct aie_partition *apart, size_t
> > > offset,
> > > +????????????????????????????????? size_t len, void *data, u32 mask)
> > > +{
> > > +???????int ret;
> > > +???????void __iomem *va;
> > > +
> > > +???????if (mask && len > sizeof(u32)) {
> > > +???????????????/* For mask write, only allow 32bit. */
> > > +???????????????dev_err(&apart->dev,
> > > +???????????????????????"failed mask write, len is more that 32bit.\n");
> > > +???????????????return -EINVAL;
> > > +???????}
> > > +
> > > +???????/* offset is expected to be relative to the start of the partition */
> > > +???????offset += aie_cal_regoff(apart->adev, apart->range.start, 0);
> > > +???????ret = aie_part_reg_validation(apart, offset, len, 1);
> > > +???????if (ret < 0) {
> > > +???????????????dev_err(&apart->dev, "failed to write to 0x%zx,0x%zx.\n",
> > > +???????????????????????offset, len);
> > > +???????????????return ret;
> > > +???????}
> > > +
> > > +???????va = apart->adev->base + offset;
> > > +???????if (!mask) {
> > > +???????????????if (len == sizeof(u32))
> > > +???????????????????????iowrite32(*((u32 *)data),? va);
> > > +???????????????else
> > > +???????????????????????memcpy_toio(va, data, len);
> > > +???????} else {
> > > +???????????????u32 val = ioread32(va);
> > > +
> > > +???????????????val &= ~mask;
> > > +???????????????val |= *((u32 *)data) & mask;
> > > +???????????????iowrite32(val, va);
> > > +???????}
> > > +
> > > +???????return (int)len;
> > > +}
> > > +
> > > +/**
> > > + * aie_part_access_regs() - AI engine partition registers access
> > > + * @apart: AI engine partition
> > > + * @num_reqs: number of access requests
> > > + * @reqs: array of registers access
> > > + * @return: 0 for success, and negative value for failure.
> > > + *
> > > + * This function executes AI engine partition register access requests.
> > > + */
> > > +static int aie_part_access_regs(struct aie_partition *apart, u32 num_reqs,
> > > +???????????????????????????????struct aie_reg_args *reqs)
> > > +{
> > > +???????u32 i;
> > > +
> > > +???????for (i = 0; i < num_reqs; i++) {
> > > +???????????????struct aie_reg_args *args = &reqs[i];
> > > +???????????????int ret;
> > > +
> > > +???????????????if (args->op != AIE_REG_WRITE) {
> > > +???????????????????????dev_err(&apart->dev,
> > > +???????????????????????????????"Invalid register command type: %u.\n",
> > > +???????????????????????????????args->op);
> > > +???????????????????????return -EINVAL;
> > > +???????????????}
> > > +???????????????ret = aie_part_write_register(apart,
> > > +???????????????????????????????????????????? (size_t)args->offset,
> > > +???????????????????????????????????????????? sizeof(args->val),
> > > +???????????????????????????????????????????? &args->val, args->mask);
> > > +???????????????if (ret < 0) {
> > > +???????????????????????dev_err(&apart->dev, "reg op %u failed: 0x%llx.\n",
> > > +???????????????????????????????args->op, args->offset);
> > > +???????????????????????return ret;
> > > +???????????????}
> > > +???????}
> > > +
> > > +???????return 0;
> > > +}
> > > +
> > > +static int aie_part_release(struct inode *inode, struct file *filp)
> > > +{
> > > +???????struct aie_partition *apart = filp->private_data;
> > > +???????int ret;
> > > +
> > > +???????/*
> > > +??????? * TODO: It will need to reset the SHIM columns and gate the
> > > +??????? * tiles of the partition.
> > > +??????? */
> > > +???????ret = mutex_lock_interruptible(&apart->mlock);
> > > +???????if (ret)
> > > +???????????????return ret;
> > > +
> > > +???????apart->status = 0;
> > > +???????mutex_unlock(&apart->mlock);
> > > +
> > > +???????return 0;
> > > +}
> > > +
> > > +static const struct vm_operations_struct aie_part_physical_vm_ops = {
> > > +#ifdef CONFIG_HAVE_IOREMAP_PROT
> > > +???????.access = generic_access_phys,
> > > +#endif
> > > +};
> > > +
> > > +static int aie_part_mmap(struct file *fp, struct vm_area_struct *vma)
> > > +{
> > > +???????struct aie_partition *apart = fp->private_data;
> > > +???????struct aie_device *adev = apart->adev;
> > > +???????unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
> > > +???????phys_addr_t addr;
> > > +???????size_t size;
> > > +
> > > +???????if (vma->vm_end < vma->vm_start)
> > > +???????????????return -EINVAL;
> > > +???????/* Only allow userspace directly read registers */
> > > +???????if (vma->vm_flags & VM_WRITE) {
> > > +???????????????dev_err(&apart->dev, "%s: do not support writable mmap.\n",
> > > +???????????????????????__func__);
> > > +???????????????return -EINVAL;
> > > +???????}
> > > +???????vma->vm_private_data = apart;
> > > +???????vma->vm_ops = &aie_part_physical_vm_ops;
> > > +???????size = apart->range.size.col << adev->col_shift;
> > > +???????if ((vma->vm_end - vma->vm_start) > (size - offset)) {
> > > +???????????????dev_err(&apart->dev,
> > > +???????????????????????"%s: size exceed.\n", __func__);
> > > +???????????????return -EINVAL;
> > > +???????}
> > > +???????vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> > > +???????/* Calculate the partition address */
> > > +???????addr = adev->res->start;
> > > +???????addr += apart->range.start.col << adev->col_shift;
> > > +???????addr += apart->range.start.row << adev->row_shift;
> > > +???????addr += offset;
> > > +???????return remap_pfn_range(vma,
> > > +????????????????????????????? vma->vm_start,
> > > +????????????????????????????? addr >> PAGE_SHIFT,
> > > +????????????????????????????? vma->vm_end - vma->vm_start,
> > > +????????????????????????????? vma->vm_page_prot);
> > > +}
> > > +
> > > +static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long
> > > arg)
> > > +{
> > > +???????struct aie_partition *apart = fp->private_data;
> > > +???????void __user *argp = (void __user *)arg;
> > > +???????long ret;
> > > +
> > > +???????switch (cmd) {
> > > +???????case AIE_REG_IOCTL:
> > > +???????{
> > > +???????????????struct aie_reg_args raccess;
> > > +
> > > +???????????????if (copy_from_user(&raccess, argp, sizeof(raccess)))
> > > +???????????????????????return -EFAULT;
> > > +
> > > +???????????????ret = mutex_lock_interruptible(&apart->mlock);
> > > +???????????????if (ret)
> > > +???????????????????????return ret;
> > > +
> > > +???????????????ret = aie_part_access_regs(apart, 1, &raccess);
> > > +???????????????mutex_unlock(&apart->mlock);
> > > +???????????????break;
> > > +???????}
> > > +???????default:
> > > +???????????????dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd);
> > > +???????????????ret = -EINVAL;
> > > +???????????????break;
> > > +???????}
> > > +
> > > +???????return ret;
> > > +}
> > > +
> > > +const struct file_operations aie_part_fops = {
> > > +???????.owner??????????= THIS_MODULE,
> > > +???????.release????????= aie_part_release,
> > > +???????.mmap???????????= aie_part_mmap,
> > > +???????.unlocked_ioctl?= aie_part_ioctl,
> > > +};
> > > +
> > > +/**
> > > + * aie_part_release_device() - release an AI engine partition instance
> > > + * @dev: AI engine partition device
> > > + *
> > > + * It will be called by device driver core when no one holds a valid
> > > + * pointer to @dev anymore.
> > > + */
> > > +static void aie_part_release_device(struct device *dev)
> > > +{
> > > +???????struct aie_partition *apart = dev_to_aiepart(dev);
> > > +???????struct aie_device *adev = apart->adev;
> > > +???????int ret;
> > > +
> > > +???????ret = mutex_lock_interruptible(&adev->mlock);
> > > +???????if (ret) {
> > > +???????????????dev_warn(&apart->dev,
> > > +??????????????????????? "getting adev->mlock is interrupted by signal\n");
> > > +???????}
> > > +
> > > +???????aie_resource_put_region(&adev->cols_res, apart->range.start.col,
> > > +???????????????????????????????apart->range.size.col);
> > > +???????list_del(&apart->node);
> > > +???????mutex_unlock(&adev->mlock);
> > > +???????put_device(apart->dev.parent);
> > > +}
> > > +
> > > +/**
> > > + * aie_create_partition() - create AI engine partition instance
> > > + * @adev: AI engine device
> > > + * @range: AI engine partition range to check. A range describes a group
> > > + *??????? of AI engine tiles.
> > > + * @return: created AI engine partition pointer for success, and PTR_ERR
> > > + *???????? for failure.
> > > + *
> > > + * This function creates an AI engine partition instance.
> > > + * It creates AI engine partition, the AI engine partition device and
> > > + * the AI engine partition character device.
> > > + */
> > > +static struct aie_partition *aie_create_partition(struct aie_device *adev,
> > > +???????????????????????????????????????????????? struct aie_range *range)
> > > +{
> > > +???????struct aie_partition *apart;
> > > +???????struct device *dev;
> > > +???????char devname[32];
> > > +???????int ret;
> > > +
> > > +???????ret = mutex_lock_interruptible(&adev->mlock);
> > > +???????if (ret)
> > > +???????????????return ERR_PTR(ret);
> > > +
> > > +???????ret = aie_resource_check_region(&adev->cols_res, range->start.col,
> > > +???????????????????????????????????????range->size.col);
> > > +???????if (ret != range->start.col) {
> > > +???????????????dev_err(&adev->dev, "invalid partition (%u,%u)(%u,%u).\n",
> > > +???????????????????????range->start.col, range->start.row,
> > > +???????????????????????range->size.col, range->size.row);
> > > +???????????????mutex_unlock(&adev->mlock);
> > > +???????????????return ERR_PTR(-EINVAL);
> > > +???????}
> > > +???????ret = aie_resource_get_region(&adev->cols_res, range->start.col,
> > > +???????????????????????????????????? range->size.col);
> > > +???????if (ret != range->start.col) {
> > > +???????????????dev_err(&adev->dev, "failed to get partition
> > > (%u,%u)(%u,%u).\n",
> > > +???????????????????????range->start.col, range->start.row,
> > > +???????????????????????range->size.col, range->size.row);
> > > +???????????????mutex_unlock(&adev->mlock);
> > > +???????????????return ERR_PTR(-EFAULT);
> > > +???????}
> > > +???????mutex_unlock(&adev->mlock);
> > > +
> > > +???????apart = devm_kzalloc(&adev->dev, sizeof(*apart), GFP_KERNEL);
> > > +???????if (!apart)
> > > +???????????????return ERR_PTR(-ENOMEM);
> > > +
> > > +???????apart->adev = adev;
> > > +???????memcpy(&apart->range, range, sizeof(*range));
> > > +???????mutex_init(&apart->mlock);
> > > +
> > > +???????/* Create AI engine partition device */
> > > +???????dev = &apart->dev;
> > > +???????device_initialize(dev);
> > > +???????dev->parent = &adev->dev;
> > > +???????dev->class = aie_class;
> > > +???????dev_set_drvdata(dev, apart);
> > > +???????snprintf(devname, sizeof(devname) - 1, "aiepart_%d_%d",
> > > +??????????????? apart->range.start.col, apart->range.size.col);
> > > +???????dev_set_name(dev, devname);
> > > +???????/* We can now rely on the release function for cleanup */
> > > +???????dev->release = aie_part_release_device;
> > > +???????ret = device_add(dev);
> > > +???????if (ret) {
> > > +???????????????dev_err(dev, "device_add failed: %d\n", ret);
> > > +???????????????put_device(dev);
> > > +???????????????return ERR_PTR(ret);
> > > +???????}
> > > +
> > > +???????ret = mutex_lock_interruptible(&adev->mlock);
> > > +???????if (ret) {
> > > +???????????????put_device(dev);
> > > +???????????????return ERR_PTR(ret);
> > > +???????}
> > > +
> > > +???????list_add_tail(&apart->node, &adev->partitions);
> > > +???????mutex_unlock(&adev->mlock);
> > > +???????get_device(&adev->dev);
> > > +???????dev_dbg(dev, "created AIE partition device.\n");
> > > +
> > > +???????return apart;
> > > +}
> > > +
> > > +struct aie_partition *
> > > +of_aie_part_probe(struct aie_device *adev, struct device_node *nc)
> > > +{
> > > +???????struct aie_partition *apart;
> > > +???????struct aie_range range;
> > > +???????u32 partition_id, regs[4];
> > > +???????int ret;
> > > +
> > > +???????/* Select device driver */
> > > +???????ret = of_property_read_u32_array(nc, "reg", regs, ARRAY_SIZE(regs));
> > > +???????if (ret < 0) {
> > > +???????????????dev_err(&adev->dev,
> > > +???????????????????????"probe %pOF failed, no tiles range information.\n",
> > > +???????????????????????nc);
> > > +???????????????return ERR_PTR(ret);
> > > +???????}
> > > +???????range.start.col = regs[0];
> > > +???????range.start.row = regs[1];
> > > +???????range.size.col = regs[2];
> > > +???????range.size.row = regs[3];
> > > +
> > > +???????ret = of_property_read_u32_index(nc, "xlnx,partition-id", 0,
> > > +??????????????????????????????????????? &partition_id);
> > > +???????if (ret < 0) {
> > > +???????????????dev_err(&adev->dev,
> > > +???????????????????????"probe %pOF failed, no partition id.\n", nc);
> > > +???????????????return ERR_PTR(ret);
> > > +???????}
> > > +
> > > +???????ret = mutex_lock_interruptible(&adev->mlock);
> > > +???????if (ret)
> > > +???????????????return ERR_PTR(ret);
> > > +
> > > +???????apart = aie_get_partition_from_id(adev, partition_id);
> > > +???????mutex_unlock(&adev->mlock);
> > > +???????if (apart) {
> > > +???????????????dev_err(&adev->dev,
> > > +???????????????????????"probe failed: partition %u exists.\n",
> > > +???????????????????????partition_id);
> > > +???????????????return ERR_PTR(ret);
> > > +???????}
> > > +
> > > +???????apart = aie_create_partition(adev, &range);
> > > +???????if (IS_ERR(apart)) {
> > > +???????????????dev_err(&adev->dev,
> > > +???????????????????????"%s: failed to create part(%u,%u),(%u,%u).\n",
> > > +???????????????????????__func__, range.start.col, range.start.row,
> > > +???????????????????????range.size.col, range.size.row);
> > > +???????????????return apart;
> > > +???????}
> > > +
> > > +???????of_node_get(nc);
> > > +???????apart->dev.of_node = nc;
> > > +???????apart->partition_id = partition_id;
> > > +
> > > +???????dev_info(&adev->dev,
> > > +??????????????? "AI engine part(%u,%u),(%u,%u), id %u is probed
> > > successfully.\n",
> > > +??????????????? range.start.col, range.start.row,
> > > +??????????????? range.size.col, range.size.row, apart->partition_id);
> > > +
> > > +???????return apart;
> > > +}
> > > +
> > > +/**
> > > + * aie_destroy_part() - destroy AI engine partition
> > > + * @apart: AI engine partition
> > > + *
> > > + * This function will remove AI engine partition.
> > > + */
> > > +void aie_part_remove(struct aie_partition *apart)
> > > +{
> > > +???????device_del(&apart->dev);
> > > +???????put_device(&apart->dev);
> > > +}
> > > diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-res.c
> > > b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
> > > new file mode 100644
> > > index 0000000..36f08bf
> > > --- /dev/null
> > > +++ b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
> > > @@ -0,0 +1,114 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Xilinx AI Engine device driver
> > > + *
> > > + * Copyright (C) 2020 Xilinx, Inc.
> > > + */
> > > +
> > > +#include <linux/bitmap.h>
> > > +
> > > +#include "ai-engine-internal.h"
> > > +
> > > +/**
> > > + * aie_resource_initialize() - initialize AI engine resource
> > > + * @res: pointer to AI engine resource
> > > + * @count: total number of element of this resource
> > > + * @return: 0 for success, negative value for failure.
> > > + *
> > > + * This function will initialize the data structure for the
> > > + * resource.
> > > + */
> > > +int aie_resource_initialize(struct aie_resource *res, int count)
> > > +{
> > > +???????if (!res || !count)
> > > +???????????????return -EINVAL;
> > > +???????res->bitmap = bitmap_zalloc(count, GFP_KERNEL);
> > > +???????if (!res->bitmap)
> > > +???????????????return -ENOMEM;
> > > +???????res->total = count;
> > > +
> > > +???????return 0;
> > > +}
> > > +
> > > +/**
> > > + * aie_resource_uninitialize() - uninitialize AI engine resource
> > > + * @res: pointer to AI engine resource
> > > + *
> > > + * This function will release the AI engine resource data members.
> > > + */
> > > +void aie_resource_uninitialize(struct aie_resource *res)
> > > +{
> > > +???????res->total = 0;
> > > +???????if (res->bitmap)
> > > +???????????????bitmap_free(res->bitmap);
> > > +}
> > > +
> > > +/**
> > > + * aie_resource_check() - check availability of requested resource
> > > + * @res: pointer to AI engine resource to check
> > > + * @start: start index of the required resource, it will only be used if
> > > + *??????? @continuous is 1. It will check the available resource starting
> > > from
> > > + *??????? @start
> > > + * @count: number of requested element
> > > + * @return: start resource id if the requested number of resources are
> > > available
> > > + *???????? It will return negative value of errors.
> > > + *
> > > + * This function will check the availability. It will return start resource
> > > id
> > > + * if the requested number of resources are available.
> > > + */
> > > +int aie_resource_check_region(struct aie_resource *res,
> > > +???????????????????????????? u32 start, u32 count)
> > > +{
> > > +???????unsigned long id;
> > > +
> > > +???????if (!res || !res->bitmap || !count)
> > > +???????????????return -EINVAL;
> > > +???????id = bitmap_find_next_zero_area(res->bitmap, res->total, start,
> > > +???????????????????????????????????????count, 0);
> > > +???????if (id >= res->total)
> > > +???????????????return -ERANGE;
> > > +
> > > +???????return (int)id;
> > > +}
> > > +
> > > +/**
> > > + * aie_resource_get_region() - get requested AI engine resource
> > > + * @res: pointer to AI engine resource to check
> > > + * @count: number of requested element
> > > + * @start: start index of the required resource
> > > + * @return: start resource id for success, and negative value for failure.
> > > + *
> > > + * This function check if the requested AI engine resource is available.
> > > + * If it is available, mark it used and return the start resource id.
> > > + */
> > > +int aie_resource_get_region(struct aie_resource *res, u32 start, u32 count)
> > > +{
> > > +???????unsigned long off;
> > > +
> > > +???????if (!res || !res->bitmap || !count)
> > > +???????????????return -EINVAL;
> > > +???????off = bitmap_find_next_zero_area(res->bitmap, res->total, start,
> > > +??????????????????????????????????????? count, 0);
> > > +???????if (off >= res->total) {
> > > +???????????????pr_err("Failed to get available AI engine resource.\n");
> > > +???????????????return -ERANGE;
> > > +???????}
> > > +???????bitmap_set(res->bitmap, off, count);
> > > +
> > > +???????return (int)off;
> > > +}
> > > +
> > > +/**
> > > + * aie_resource_put_region() - release requested AI engine resource
> > > + * @res: pointer to AI engine resource to check
> > > + * @start: start index of the resource to release
> > > + * @count: number of elements to release
> > > + *
> > > + * This function release the requested AI engine resource.
> > > + */
> > > +void aie_resource_put_region(struct aie_resource *res, int start, u32 count)
> > > +{
> > > +???????if (!res || !count)
> > > +???????????????return;
> > > +???????bitmap_clear(res->bitmap, start, count);
> > > +}
> > > diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-
> > > engine.h
> > > new file mode 100644
> > > index 0000000..acbc781
> > > --- /dev/null
> > > +++ b/include/uapi/linux/xlnx-ai-engine.h
> > > @@ -0,0 +1,107 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > > +/*
> > > + * Copyright (c) 2020, Xilinx Inc.
> > > + */
> > > +
> > > +#ifndef _UAPI_AI_ENGINE_H_
> > > +#define _UAPI_AI_ENGINE_H_
> > > +
> > > +#include <linux/ioctl.h>
> > > +#include <linux/types.h>
> > > +
> > > +enum aie_reg_op {
> > > +???????AIE_REG_WRITE,
> > > +};
> > > +
> > > +/* AI engine partition is in use */
> > > +#define XAIE_PART_STATUS_INUSE?????????(1U << 0)
> > > +
> > > +/**
> > > + * struct aie_location - AIE location information
> > > + * @col: column id
> > > + * @row: row id
> > > + */
> > > +struct aie_location {
> > > +???????__u32 col;
> > > +???????__u32 row;
> > > +};
> > > +
> > > +/**
> > > + * struct aie_range - AIE range information
> > > + * @start: start tile location
> > > + * @size: size of the range, number of columns and rows
> > > + */
> > > +struct aie_range {
> > > +???????struct aie_location start;
> > > +???????struct aie_location size;
> > > +};
> > > +
> > > +/**
> > > + * struct aie_reg_args - AIE access register arguments
> > > + * @op: if this request is to read, write or poll register
> > > + * @mask: mask for mask write, 0 for not mask write
> > > + * @offset: offset of register to the start of an AI engine partition
> > > + * @val: value to write or get
> > > + */
> > > +struct aie_reg_args {
> > > +???????enum aie_reg_op op;
> > > +???????__u32 mask;
> > > +???????__u64 offset;
> > > +???????__u32 val;
> > > +};
> > > +
> > > +/**
> > > + * struct aie_range_args - AIE range request arguments
> > > + * @partition_id: partition id. It is used to identify the
> > > + *?????????????? AI engine partition in the system.
> > > + * @uid: image identifier loaded on the AI engine partition
> > > + * @range: range of AIE tiles
> > > + * @status: indicate if the AI engine is in use.
> > > + *???????? 0 means not in used, otherwise, in use.
> > > + */
> > > +struct aie_range_args {
> > > +???????__u32 partition_id;
> > > +???????__u32 uid;
> > > +???????struct aie_range range;
> > > +???????__u32 status;
> > > +};
> > > +
> > > +/**
> > > + * struct aie_partition_query - AIE partition query arguments
> > > + * @partition_cnt: number of defined partitions in the system
> > > + * @partitions: buffer to store defined partitions information.
> > > + */
> > > +struct aie_partition_query {
> > > +???????struct aie_range_args *partitions;
> > > +???????__u32 partition_cnt;
> > > +};
> > > +
> > > +/**
> > > + * struct aie_partition_req - AIE request partition arguments
> > > + * @partition_id: partition node id. It is used to identify the AI engine
> > > + *?????????????? partition in the system.
> > > + * @uid: image identifier loaded on the AI engine partition
> > > + * @meta_data: meta data to indicate which resources used by application.
> > > + * @flag: used for application to indicate particular driver requirements
> > > + *?????? application wants to have for the partition. e.g. do not clean
> > > + *?????? resource when closing the partition.
> > > + */
> > > +struct aie_partition_req {
> > > +???????__u32 partition_id;
> > > +???????__u32 uid;
> > > +???????__u64 meta_data;
> > > +???????__u32 flag;
> > > +};
> > > +
> > > +#define AIE_IOCTL_BASE 'A'
> > > +
> > > +/* AI engine device IOCTL operations */
> > > +#define AIE_ENQUIRE_PART_IOCTL?????????_IOWR(AIE_IOCTL_BASE, 0x1, \
> > > +???????????????????????????????????????????? struct aie_partition_query)
> > > +#define AIE_REQUEST_PART_IOCTL?????????_IOR(AIE_IOCTL_BASE, 0x2, \
> > > +??????????????????????????????????????????? struct aie_partition_req)
> > > +
> > > +/* AI engine partition IOCTL operations */
> > > +#define AIE_REG_IOCTL??????????????????_IOWR(AIE_IOCTL_BASE, 0x8, \
> > > +???????????????????????????????????????????? struct aie_reg_args)
> > > +#endif
> >
> _______________________________________________
> dri-devel mailing list
> [email protected]
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

2020-12-13 14:04:55

by Wendy Liang

[permalink] [raw]
Subject: Re: [PATCH 2/9] misc: Add Xilinx AI engine device driver


On 12/9/20 4:47 AM, Daniel Vetter wrote:
> On Tue, Dec 08, 2020 at 11:54:57AM -0800, Jiaying Liang wrote:
>> On 12/8/20 9:12 AM, Nicolas Dufresne wrote:
>>> Le mercredi 18 novembre 2020 à 00:06 -0800, Wendy Liang a écrit :
>>>> Create AI engine device/partition hierarchical structure.
>>>>
>>>> Each AI engine device can have multiple logical partitions(groups of AI
>>>> engine tiles). Each partition is column based and has its own node ID
>>>> in the system. AI engine device driver manages its partitions.
>>>>
>>>> Applications can access AI engine partition through the AI engine
>>>> partition driver instance. AI engine registers write is moved to kernel
>>>> as there are registers in the AI engine array needs privilege
>>>> permission.
>>> Hi there, it's nice to see an effort to upstream an AI driver. I'm a little
>>> worried this driver is not obvious to use from it's source code itself. So you
>>> have reference to some Open Source code that demonstrate it's usage ?
>> We have AI engine library which provides a cross platforms APIs for other
>>
>> libraries/application to use the hardware. Here is the source code:
>>
>> https://github.com/Xilinx/embeddedsw/tree/master/XilinxProcessorIPLib/drivers/aienginev2/src
>>
>> The cross platforms AI engine library runs in LInux userspace it defines how
>> to
>>
>> configure, and the kernel driver controls if what can be access and manage
>> errors from device.
> So I kinda ignored this driver submission because in the past all these AI
> drivers had at best incomplete open source (usually the compiler is
> closed, often also large parts of the runtime). I think yours would be the
> first that breaks this trend, is that really the case? I.e. I could make
> full use of this hw without any closed source bits to run DNN workloads
> and things like that?
AI engine can be used for signaling processing or high performance
computing

the kernel driver works on the AI engine software library which I
mentioned above,

which will be used by Xilinx runtime:
https://xilinx.github.io/XRT/2020.2/html/index.html

Xilinx runtime is a layer for acceleration libraries or applications to
use Xilinx accelerators.

e.g. it has OpenCL implementation
> If that's the case then I think there's nothing stopping us from doing the
> right thing and merging this driver into the right subsystem: The
> subsystem for accelerators which their own memory and who want dma-buf
> integration is drivers/gpu, not drivers/misc.

The AI engine kernel driver is used for device runtime configuration update,

and runtime monitoring, such as async errors detection. The buffer
management

is out of the AI engine driver, but it is covered by Xilinx runtime:

https://github.com/Xilinx/XRT/tree/master/src/runtime_src/core/edge/drm/zocl

AI engine driver imports the DMA buf.


The AI engine device is quite different to the GPU devices. The AI engine

operations are still needs driver specific ioctls.

We have more than 100 cores tiles, each tiles functions can be defined
at compilation

time, at runtime, we load the configuration (application defined I/O
commands) to

configure each tiles registers to set up routing, set up DMAs, configure
local memories,

and enable the tiles.


As the AI engine device hardware is different to the GPUs,

we are not able to make use of functions abstracted for GPUs, and we
don't manage the

buffers in this driver. I am not sure if it is ok to add the driver to
drivers/gpu but not

using abstractions from the GPU abstraction.


There is another reply to the patch series to ask for clarification on
the overview of the

driver, and I had some discussions with other team members. I will reply
to that email

to provide more details on overall how this driver is used.

Any suggestions on how to fit the driver in the drivers/gpu or other
drivers framework

will be much appreciated.


Thanks,

Wendy

> Apologies that I'm jumping with the really big arch review when v3 is
> already on the list. But last few times merging AI drivers to drivers/misc
> was really just a way to avoid the merge criteria for drivers/gpu
> acceleration drivers. I'd love to land the first real open AI driver in
> upstream, properly.
>
> Cheers, Daniel
>
>
>
>> Best Regards,
>>
>> Wendy
>>
>>
>>>> Signed-off-by: Wendy Liang<[email protected]>
>>>> Signed-off-by: Hyun Kwon<[email protected]>
>>>> ---
>>>>  MAINTAINERS                                        |   8 +
>>>>  drivers/misc/Kconfig                               |  12 +
>>>>  drivers/misc/Makefile                              |   1 +
>>>>  drivers/misc/xilinx-ai-engine/Makefile             |  11 +
>>>>  drivers/misc/xilinx-ai-engine/ai-engine-aie.c      | 115 +++++
>>>>  drivers/misc/xilinx-ai-engine/ai-engine-dev.c      | 448 ++++++++++++++++++
>>>>  drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 226 ++++++++++
>>>>  drivers/misc/xilinx-ai-engine/ai-engine-part.c     | 498
>>>> +++++++++++++++++++++
>>>>  drivers/misc/xilinx-ai-engine/ai-engine-res.c      | 114 +++++
>>>>  include/uapi/linux/xlnx-ai-engine.h                | 107 +++++
>>>>  10 files changed, 1540 insertions(+)
>>>>  create mode 100644 drivers/misc/xilinx-ai-engine/Makefile
>>>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>>>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>>>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>>>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-part.c
>>>>  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-res.c
>>>>  create mode 100644 include/uapi/linux/xlnx-ai-engine.h
>>>>
>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>> index 5cc595a..40e3351 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -19283,6 +19283,14 @@ T:     githttps://github.com/Xilinx/linux-xlnx.git
>>>>  F:     Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml
>>>>  F:     drivers/phy/xilinx/phy-zynqmp.c
>>>> +XILINX AI ENGINE DRIVER
>>>> +M:     Wendy Liang<[email protected]>
>>>> +S:     Supported
>>>> +F:     Documentation/devicetree/bindings/soc/xilinx/xlnx,ai-engine.yaml
>>>> +F:     drivers/misc/xilinx-ai-engine/
>>>> +F:     include/linux/xlnx-ai-engine.h
>>>> +F:     include/uapi/linux/xlnx-ai-engine.h
>>>> +
>>>>  XILLYBUS DRIVER
>>>>  M:     Eli Billauer<[email protected]>
>>>>  L:[email protected]
>>>> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
>>>> index fafa8b0..0b8ce4d 100644
>>>> --- a/drivers/misc/Kconfig
>>>> +++ b/drivers/misc/Kconfig
>>>> @@ -444,6 +444,18 @@ config XILINX_SDFEC
>>>>           If unsure, say N.
>>>> +config XILINX_AIE
>>>> +       tristate "Xilinx AI engine"
>>>> +       depends on ARM64 || COMPILE_TEST
>>>> +       help
>>>> +         This option enables support for the Xilinx AI engine driver.
>>>> +         One Xilinx AI engine device can have multiple partitions (groups of
>>>> +         AI engine tiles). Xilinx AI engine device driver instance manages
>>>> +         AI engine partitions. User application access its partitions through
>>>> +         AI engine partition instance file operations.
>>>> +
>>>> +         If unsure, say N
>>>> +
>>>>  config MISC_RTSX
>>>>         tristate
>>>>         default MISC_RTSX_PCI || MISC_RTSX_USB
>>>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>>>> index d23231e..2176b18 100644
>>>> --- a/drivers/misc/Makefile
>>>> +++ b/drivers/misc/Makefile
>>>> @@ -57,3 +57,4 @@ obj-$(CONFIG_HABANA_AI)               += habanalabs/
>>>>  obj-$(CONFIG_UACCE)            += uacce/
>>>>  obj-$(CONFIG_XILINX_SDFEC)     += xilinx_sdfec.o
>>>>  obj-$(CONFIG_HISI_HIKEY_USB)   += hisi_hikey_usb.o
>>>> +obj-$(CONFIG_XILINX_AIE)       += xilinx-ai-engine/
>>>> diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-
>>>> engine/Makefile
>>>> new file mode 100644
>>>> index 0000000..7827a0a
>>>> --- /dev/null
>>>> +++ b/drivers/misc/xilinx-ai-engine/Makefile
>>>> @@ -0,0 +1,11 @@
>>>> +# SPDX-License-Identifier: GPL-2.0-only
>>>> +#
>>>> +# Makefile for Xilinx AI engine device driver
>>>> +#
>>>> +
>>>> +obj-$(CONFIG_XILINX_AIE)       += xilinx-aie.o
>>>> +
>>>> +xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \
>>>> +                                  ai-engine-dev.o \
>>>> +                                  ai-engine-part.o \
>>>> +                                  ai-engine-res.o
>>>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>>>> b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>>>> new file mode 100644
>>>> index 0000000..319260f
>>>> --- /dev/null
>>>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
>>>> @@ -0,0 +1,115 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Xilinx AI Engine driver AIE device specific implementation
>>>> + *
>>>> + * Copyright (C) 2020 Xilinx, Inc.
>>>> + */
>>>> +
>>>> +#include <linux/slab.h>
>>>> +
>>>> +#include "ai-engine-internal.h"
>>>> +
>>>> +#define AIE_ARRAY_SHIFT                30U
>>>> +#define AIE_COL_SHIFT          23U
>>>> +#define AIE_ROW_SHIFT          18U
>>>> +
>>>> +/*
>>>> + * Registers offsets
>>>> + */
>>>> +#define AIE_SHIMNOC_L2INTR_MASK_REGOFF         0x00015000U
>>>> +#define AIE_SHIMNOC_L2INTR_INTR_REGOFF         0x00015010U
>>>> +#define AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF     0x0001d000U
>>>> +#define AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF     0x0001d13cU
>>>> +#define AIE_SHIMNOC_AXIMM_REGOFF               0x0001e020U
>>>> +#define AIE_SHIMPL_L1INTR_MASK_A_REGOFF                0x00035000U
>>>> +#define AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF 0x00035050U
>>>> +#define AIE_SHIMPL_CLKCNTR_REGOFF              0x00036040U
>>>> +#define AIE_SHIMPL_RESET_REGOFF                        0x0003604cU
>>>> +#define AIE_TILE_CORE_CLKCNTR_REGOFF           0x00036040U
>>>> +
>>>> +static const struct aie_tile_regs aie_kernel_regs[] = {
>>>> +       /* SHIM AXI MM Config */
>>>> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>>>> +        .soff = AIE_SHIMNOC_AXIMM_REGOFF,
>>>> +        .eoff = AIE_SHIMNOC_AXIMM_REGOFF,
>>>> +       },
>>>> +       /* SHIM DMA ADDRESS range */
>>>> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>>>> +        .soff = AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF,
>>>> +        .eoff = AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF,
>>>> +       },
>>>> +       /* SHIM 2nd level interrupt controller */
>>>> +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>>>> +        .soff = AIE_SHIMNOC_L2INTR_MASK_REGOFF,
>>>> +        .eoff = AIE_SHIMNOC_L2INTR_INTR_REGOFF,
>>>> +       },
>>>> +       /* SHIM 1st level interrupt controller */
>>>> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
>>>> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>>>> +        .soff = AIE_SHIMPL_L1INTR_MASK_A_REGOFF,
>>>> +        .eoff = AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF,
>>>> +       },
>>>> +       /* SHIM reset Enable */
>>>> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
>>>> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>>>> +        .soff = AIE_SHIMPL_RESET_REGOFF,
>>>> +        .eoff = AIE_SHIMPL_RESET_REGOFF,
>>>> +       },
>>>> +       /* SHIM clock control */
>>>> +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
>>>> +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>>>> +        .soff = AIE_SHIMPL_CLKCNTR_REGOFF,
>>>> +        .eoff = AIE_SHIMPL_CLKCNTR_REGOFF,
>>>> +       },
>>>> +       /* Tile clock control */
>>>> +       {.attribute = AIE_TILE_TYPE_TILE << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
>>>> +        .soff = AIE_TILE_CORE_CLKCNTR_REGOFF,
>>>> +        .eoff = AIE_TILE_CORE_CLKCNTR_REGOFF,
>>>> +       },
>>>> +};
>>>> +
>>>> +static u32 aie_get_tile_type(struct aie_location *loc)
>>>> +{
>>>> +       if (loc->row)
>>>> +               return AIE_TILE_TYPE_TILE;
>>>> +       /* SHIM row */
>>>> +       if ((loc->col % 4) < 2)
>>>> +               return AIE_TILE_TYPE_SHIMPL;
>>>> +
>>>> +       return AIE_TILE_TYPE_SHIMNOC;
>>>> +}
>>>> +
>>>> +static const struct aie_tile_operations aie_ops = {
>>>> +       .get_tile_type = aie_get_tile_type,
>>>> +};
>>>> +
>>>> +/**
>>>> + * aie_device_init() - Initialize AI engine device struct AIE specific
>>>> + *                    properties
>>>> + * @adev: AI engine device
>>>> + * @return: 0 for success, negative value for failure.
>>>> + *
>>>> + * This function initialize the AI engine device structure device version
>>>> + * specific elements such as register addressing related array shift,
>>>> + * column shift, and row shift; AIE specific device operations, device
>>>> + * columns resource.
>>>> + */
>>>> +int aie_device_init(struct aie_device *adev)
>>>> +{
>>>> +       int ret;
>>>> +
>>>> +       adev->array_shift = AIE_ARRAY_SHIFT;
>>>> +       adev->col_shift = AIE_COL_SHIFT;
>>>> +       adev->row_shift = AIE_ROW_SHIFT;
>>>> +       adev->ops = &aie_ops;
>>>> +       adev->num_kernel_regs = ARRAY_SIZE(aie_kernel_regs);
>>>> +       adev->kernel_regs = aie_kernel_regs;
>>>> +
>>>> +       /* Get the columns resource */
>>>> +       /* Get number of columns from AI engine memory resource */
>>>> +       ret = aie_resource_initialize(&adev->cols_res, 50);
>>>> +       if (ret)
>>>> +               dev_err(&adev->dev, "failed to initialize columns
>>>> resource.\n");
>>>> +
>>>> +       return ret;
>>>> +}
>>>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>>>> b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>>>> new file mode 100644
>>>> index 0000000..2ab2dc8
>>>> --- /dev/null
>>>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
>>>> @@ -0,0 +1,448 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Xilinx AI Engine device driver
>>>> + *
>>>> + * Copyright (C) 2020 Xilinx, Inc.
>>>> + */
>>>> +
>>>> +#include <linux/anon_inodes.h>
>>>> +#include <linux/cdev.h>
>>>> +#include <linux/delay.h>
>>>> +#include <linux/device.h>
>>>> +#include <linux/dma-mapping.h>
>>>> +#include <linux/file.h>
>>>> +#include <linux/fs.h>
>>>> +#include <linux/idr.h>
>>>> +#include <linux/list.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/mutex.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/of_device.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/uaccess.h>
>>>> +#include <uapi/linux/xlnx-ai-engine.h>
>>>> +
>>>> +#include "ai-engine-internal.h"
>>>> +
>>>> +#define AIE_DEV_MAX    (MINORMASK + 1)
>>>> +
>>>> +static dev_t aie_major;
>>>> +struct class *aie_class;
>>>> +
>>>> +static DEFINE_IDA(aie_device_ida);
>>>> +static DEFINE_IDA(aie_minor_ida);
>>>> +
>>>> +/**
>>>> + * aie_get_partition_fd() - Get AI engine partition file descriptor
>>>> + * @apart: AI engine partition
>>>> + * @return: file descriptor for AI engine partition for success, or negative
>>>> + *         value for failure.
>>>> + *
>>>> + * This function gets a file descriptor for the AI engine partition.
>>>> + */
>>>> +static int aie_get_partition_fd(struct aie_partition *apart)
>>>> +{
>>>> +       struct file *filep;
>>>> +       int ret;
>>>> +
>>>> +       /*
>>>> +        * We can't use anon_inode_getfd() because we need to modify
>>>> +        * the f_mode flags directly to allow more than just ioctls
>>>> +        */
>>>> +       ret = get_unused_fd_flags(O_CLOEXEC);
>>>> +       if (ret < 0)
>>>> +               return ret;
>>>> +
>>>> +       filep = anon_inode_getfile(dev_name(&apart->dev), &aie_part_fops,
>>>> +                                  apart, O_RDWR);
>>>> +       if (IS_ERR(filep)) {
>>>> +               put_unused_fd(ret);
>>>> +               ret = PTR_ERR(filep);
>>>> +               return ret;
>>>> +       }
>>>> +       filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
>>>> +       fd_install(ret, filep);
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_enquire_partitions() - get AI engine partitions information
>>>> + * @adev: AI engine device
>>>> + * @query: data struct to store the partition information
>>>> + * @return: 0 for success, and negative value for failure.
>>>> + */
>>>> +static int aie_enquire_partitions(struct aie_device *adev,
>>>> +                                 struct aie_partition_query *query)
>>>> +{
>>>> +       struct aie_partition *apart;
>>>> +       u32 partition_cnt, i = 0;
>>>> +       int ret;
>>>> +
>>>> +       if (!query->partitions) {
>>>> +               /*
>>>> +                * If partitions information buffer is NULL.
>>>> +                * It is to get the number of partitions.
>>>> +                */
>>>> +               query->partition_cnt = 0;
>>>> +               list_for_each_entry(apart, &adev->partitions, node)
>>>> +                       query->partition_cnt++;
>>>> +               return 0;
>>>> +       }
>>>> +
>>>> +       partition_cnt = query->partition_cnt;
>>>> +       if (!partition_cnt)
>>>> +               return 0;
>>>> +
>>>> +       ret = mutex_lock_interruptible(&adev->mlock);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       list_for_each_entry(apart, &adev->partitions, node) {
>>>> +               struct aie_range_args part;
>>>> +
>>>> +               if (i >= partition_cnt)
>>>> +                       break;
>>>> +               part.partition_id = apart->partition_id;
>>>> +               /*
>>>> +                * TBD: check with PLM that if the partition is programmed
>>>> +                * and get the UID of the image which is loaded on the AI
>>>> +                * engine partition.
>>>> +                */
>>>> +               part.uid = 0;
>>>> +               part.range.start.col = apart->range.start.col;
>>>> +               part.range.start.row = apart->range.start.row;
>>>> +               part.range.size.col = apart->range.size.col;
>>>> +               part.range.size.row = apart->range.size.row;
>>>> +               /* Check if partition is in use */
>>>> +               part.status = apart->status;
>>>> +               if (copy_to_user((void __user *)&query->partitions[i], &part,
>>>> +                                sizeof(part))) {
>>>> +                       mutex_unlock(&adev->mlock);
>>>> +                       return -EFAULT;
>>>> +               }
>>>> +               i++;
>>>> +       }
>>>> +       mutex_unlock(&adev->mlock);
>>>> +       query->partition_cnt = i;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_get_partition_from_id() - get AI engine partition from id
>>>> + * @adev: AI engine device
>>>> + * @partition_id: partition id to check
>>>> + * @return: partition pointer if partition exists, otherwise, NULL.
>>>> + *
>>>> + * This function checks defined partitions with partition id.
>>>> + * This function expect the caller to lock mlock of @adev.
>>>> + */
>>>> +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
>>>> +                                               u32 partition_id)
>>>> +{
>>>> +       struct aie_partition *apart;
>>>> +
>>>> +       list_for_each_entry(apart, &adev->partitions, node) {
>>>> +               if (apart->partition_id == partition_id)
>>>> +                       return apart;
>>>> +       }
>>>> +
>>>> +       return NULL;
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_request_partition() - request AI engine partition
>>>> + * @adev: AI engine device
>>>> + * @req: partition request, includes the requested AI engine information
>>>> + *      such as partition node ID and the UID of the image which is
>>>> + *      loaded on the partition.
>>>> + * @return: partition pointer if partition exists, otherwise, NULL.
>>>> + *
>>>> + * This function finds a defined partition which matches the specified
>>>> + * partition id, request it if it hasn't been requested, and returns it.
>>>> + */
>>>> +struct aie_partition *aie_request_partition(struct aie_device *adev,
>>>> +                                           struct aie_partition_req *req)
>>>> +{
>>>> +       struct aie_partition *apart;
>>>> +       int ret;
>>>> +
>>>> +       ret = mutex_lock_interruptible(&adev->mlock);
>>>> +       if (ret)
>>>> +               return ERR_PTR(ret);
>>>> +
>>>> +       apart = aie_get_partition_from_id(adev, req->partition_id);
>>>> +       if (!apart) {
>>>> +               dev_err(&adev->dev,
>>>> +                       "request partition %u failed, not exist.\n",
>>>> +                       req->partition_id);
>>>> +               mutex_unlock(&adev->mlock);
>>>> +               return ERR_PTR(-EINVAL);
>>>> +       }
>>>> +       /*
>>>> +        * TBD: It will check image UID too to see if the user matches
>>>> +        * what's loaded in the AI engine partition. And check the meta
>>>> +        * data to see which resources used by application.
>>>> +        */
>>>> +
>>>> +       ret = mutex_lock_interruptible(&apart->mlock);
>>>> +       if (ret)
>>>> +               return ERR_PTR(ret);
>>>> +
>>>> +       if (apart->status & XAIE_PART_STATUS_INUSE) {
>>>> +               mutex_unlock(&apart->mlock);
>>>> +               dev_err(&adev->dev,
>>>> +                       "request partition %u failed, partition in use.\n",
>>>> +                       req->partition_id);
>>>> +               apart = ERR_PTR(-EBUSY);
>>>> +       } else {
>>>> +               /*
>>>> +                * TBD:
>>>> +                * 1. setup NOC AXI MM config to only generate error events
>>>> +                *    for slave error and decode error.
>>>> +                * 2. scan to see which tiles have been clock gated.
>>>> +                *
>>>> +                * This needs to be done before the AI engine partition is
>>>> +                * exported for user to access.
>>>> +                */
>>>> +               apart->status = XAIE_PART_STATUS_INUSE;
>>>> +               mutex_unlock(&apart->mlock);
>>>> +       }
>>>> +       mutex_unlock(&adev->mlock);
>>>> +
>>>> +       return apart;
>>>> +}
>>>> +
>>>> +static long xilinx_ai_engine_ioctl(struct file *filp, unsigned int cmd,
>>>> +                                  unsigned long arg)
>>>> +{
>>>> +       struct inode *inode = file_inode(filp);
>>>> +       struct aie_device *adev = cdev_to_aiedev(inode->i_cdev);
>>>> +       void __user *argp = (void __user *)arg;
>>>> +       int ret;
>>>> +
>>>> +       switch (cmd) {
>>>> +       case AIE_ENQUIRE_PART_IOCTL:
>>>> +       {
>>>> +               struct aie_partition_query query;
>>>> +               struct aie_partition_query  __user *uquery_ptr = argp;
>>>> +
>>>> +               if (copy_from_user(&query, uquery_ptr, sizeof(query)))
>>>> +                       return -EFAULT;
>>>> +               ret = aie_enquire_partitions(adev, &query);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               if (copy_to_user((void __user *)&uquery_ptr->partition_cnt,
>>>> +                                &query.partition_cnt,
>>>> +                                sizeof(query.partition_cnt)))
>>>> +                       return -EFAULT;
>>>> +               break;
>>>> +       }
>>>> +       case AIE_REQUEST_PART_IOCTL:
>>>> +       {
>>>> +               struct aie_partition_req req;
>>>> +               struct aie_partition *apart;
>>>> +
>>>> +               if (copy_from_user(&req, argp, sizeof(req)))
>>>> +                       return -EFAULT;
>>>> +               apart = aie_request_partition(adev, &req);
>>>> +               if (IS_ERR(apart))
>>>> +                       return PTR_ERR(apart);
>>>> +               ret = aie_get_partition_fd(apart);
>>>> +               if (ret < 0) {
>>>> +                       dev_err(&apart->dev, "failed to get fd.\n");
>>>> +                       break;
>>>> +               }
>>>> +               break;
>>>> +       }
>>>> +       default:
>>>> +               dev_err(&adev->dev, "Invalid ioctl command %u.\n", cmd);
>>>> +               ret = -EINVAL;
>>>> +               break;
>>>> +       }
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static const struct file_operations aie_device_fops = {
>>>> +       .owner          = THIS_MODULE,
>>>> +       .unlocked_ioctl = xilinx_ai_engine_ioctl,
>>>> +};
>>>> +
>>>> +static void xilinx_ai_engine_release_device(struct device *dev)
>>>> +{
>>>> +       struct aie_device *adev = dev_to_aiedev(dev);
>>>> +
>>>> +       ida_simple_remove(&aie_device_ida, dev->id);
>>>> +       ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
>>>> +       cdev_del(&adev->cdev);
>>>> +       aie_resource_uninitialize(&adev->cols_res);
>>>> +}
>>>> +
>>>> +/**
>>>> + * of_xilinx_ai_engine_part_probe() - probes for AI engine partition nodes
>>>> + * @adev: AI engine device
>>>> + *
>>>> + * This function will probe for children AI engine partition nodes and create
>>>> + * an AI engine partition instance for each node.
>>>> + */
>>>> +static void of_xilinx_ai_engine_part_probe(struct aie_device *adev)
>>>> +{
>>>> +       struct device_node *nc;
>>>> +
>>>> +       for_each_available_child_of_node(adev->dev.of_node, nc) {
>>>> +               struct aie_partition *apart;
>>>> +
>>>> +               if (of_node_test_and_set_flag(nc, OF_POPULATED))
>>>> +                       continue;
>>>> +               apart = of_aie_part_probe(adev, nc);
>>>> +               if (IS_ERR(apart)) {
>>>> +                       dev_err(&adev->dev,
>>>> +                               "Failed to probe AI engine part for %pOF\n",
>>>> +                               nc);
>>>> +                       of_node_clear_flag(nc, OF_POPULATED);
>>>> +               }
>>>> +       }
>>>> +}
>>>> +
>>>> +static int xilinx_ai_engine_probe(struct platform_device *pdev)
>>>> +{
>>>> +       struct aie_device *adev;
>>>> +       struct device *dev;
>>>> +       int ret;
>>>> +
>>>> +       adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
>>>> +       if (!adev)
>>>> +               return -ENOMEM;
>>>> +       platform_set_drvdata(pdev, adev);
>>>> +       INIT_LIST_HEAD(&adev->partitions);
>>>> +       mutex_init(&adev->mlock);
>>>> +
>>>> +       adev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>> +       if (!adev->res) {
>>>> +               dev_err(&pdev->dev, "No memory resource.\n");
>>>> +               return -EINVAL;
>>>> +       }
>>>> +       adev->base = devm_ioremap_resource(&pdev->dev, adev->res);
>>>> +       if (IS_ERR(adev->base)) {
>>>> +               dev_err(&pdev->dev, "no io memory resource.\n");
>>>> +               return PTR_ERR(adev->base);
>>>> +       }
>>>> +
>>>> +       /* Initialize AIE device specific instance. */
>>>> +       ret = aie_device_init(adev);
>>>> +       if (ret < 0) {
>>>> +               dev_err(&pdev->dev, "failed to initialize device
>>>> instance.\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       dev = &adev->dev;
>>>> +       device_initialize(dev);
>>>> +       dev->class = aie_class;
>>>> +       dev->parent = &pdev->dev;
>>>> +       dev->of_node = pdev->dev.of_node;
>>>> +
>>>> +       ret = ida_simple_get(&aie_minor_ida, 0, AIE_DEV_MAX, GFP_KERNEL);
>>>> +       if (ret < 0)
>>>> +               goto free_dev;
>>>> +       dev->devt = MKDEV(MAJOR(aie_major), ret);
>>>> +       ret = ida_simple_get(&aie_device_ida, 0, 0, GFP_KERNEL);
>>>> +       if (ret < 0)
>>>> +               goto free_minor_ida;
>>>> +       dev->id = ret;
>>>> +       dev_set_name(&adev->dev, "aie%d", dev->id);
>>>> +
>>>> +       cdev_init(&adev->cdev, &aie_device_fops);
>>>> +       adev->cdev.owner = THIS_MODULE;
>>>> +       ret = cdev_add(&adev->cdev, dev->devt, 1);
>>>> +       if (ret)
>>>> +               goto free_ida;
>>>> +       /* We can now rely on the release function for cleanup */
>>>> +       dev->release = xilinx_ai_engine_release_device;
>>>> +
>>>> +       ret = device_add(dev);
>>>> +       if (ret) {
>>>> +               dev_err(&pdev->dev, "device_add failed: %d\n", ret);
>>>> +               put_device(dev);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       of_xilinx_ai_engine_part_probe(adev);
>>>> +       dev_info(&pdev->dev, "Xilinx AI Engine device(cols=%u) probed\n",
>>>> +                adev->cols_res.total);
>>>> +       return 0;
>>>> +
>>>> +free_ida:
>>>> +       ida_simple_remove(&aie_device_ida, dev->id);
>>>> +free_minor_ida:
>>>> +       ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
>>>> +free_dev:
>>>> +       put_device(dev);
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static int xilinx_ai_engine_remove(struct platform_device *pdev)
>>>> +{
>>>> +       struct aie_device *adev = platform_get_drvdata(pdev);
>>>> +       struct aie_partition *apart;
>>>> +
>>>> +       list_for_each_entry(apart, &adev->partitions, node)
>>>> +               aie_part_remove(apart);
>>>> +
>>>> +       device_del(&adev->dev);
>>>> +       put_device(&adev->dev);
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static const struct of_device_id xilinx_ai_engine_of_match[] = {
>>>> +       { .compatible = "xlnx,ai-engine-v1.0", },
>>>> +       { /* end of table */ },
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, xilinx_ai_engine_of_match);
>>>> +
>>>> +static struct platform_driver xilinx_ai_engine_driver = {
>>>> +       .probe                  = xilinx_ai_engine_probe,
>>>> +       .remove                 = xilinx_ai_engine_remove,
>>>> +       .driver                 = {
>>>> +               .name           = "xilinx-ai-engine",
>>>> +               .of_match_table = xilinx_ai_engine_of_match,
>>>> +       },
>>>> +};
>>>> +
>>>> +static int __init xilinx_ai_engine_init(void)
>>>> +{
>>>> +       int ret;
>>>> +
>>>> +       ret = alloc_chrdev_region(&aie_major, 0, AIE_DEV_MAX, "aie");
>>>> +       if (ret < 0) {
>>>> +               pr_err("aie: failed to allocate aie region\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       aie_class = class_create(THIS_MODULE, "aie");
>>>> +       if (IS_ERR(aie_class)) {
>>>> +               pr_err("failed to create aie class\n");
>>>> +               unregister_chrdev_region(aie_major, AIE_DEV_MAX);
>>>> +               return PTR_ERR(aie_class);
>>>> +       }
>>>> +
>>>> +       platform_driver_register(&xilinx_ai_engine_driver);
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +postcore_initcall(xilinx_ai_engine_init);
>>>> +
>>>> +static void __exit xilinx_ai_engine_exit(void)
>>>> +{
>>>> +       platform_driver_unregister(&xilinx_ai_engine_driver);
>>>> +       class_destroy(aie_class);
>>>> +       unregister_chrdev_region(aie_major, AIE_DEV_MAX);
>>>> +}
>>>> +module_exit(xilinx_ai_engine_exit);
>>>> +
>>>> +MODULE_AUTHOR("Xilinx, Inc.");
>>>> +MODULE_LICENSE("GPL v2");
>>>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>>>> b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>>>> new file mode 100644
>>>> index 0000000..6a69946
>>>> --- /dev/null
>>>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
>>>> @@ -0,0 +1,226 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0-only */
>>>> +/*
>>>> + * Xilinx AI Engine driver internal header
>>>> + *
>>>> + * Copyright (C) 2020 Xilinx, Inc.
>>>> + */
>>>> +
>>>> +#ifndef AIE_INTERNAL_H
>>>> +#define AIE_INTERNAL_H
>>>> +
>>>> +#include <linux/bitfield.h>
>>>> +#include <linux/bits.h>
>>>> +#include <linux/cdev.h>
>>>> +#include <linux/device.h>
>>>> +#include <linux/io.h>
>>>> +#include <linux/list.h>
>>>> +#include <linux/mutex.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/of_device.h>
>>>> +#include <uapi/linux/xlnx-ai-engine.h>
>>>> +
>>>> +/*
>>>> + * Macros for AI engine tile type bitmasks
>>>> + */
>>>> +#define AIE_TILE_TYPE_TILE     BIT(0)
>>>> +#define AIE_TILE_TYPE_SHIMPL   BIT(1)
>>>> +/* SHIM NOC tile includes SHIM PL and SHIM NOC modules */
>>>> +#define AIE_TILE_TYPE_SHIMNOC  BIT(2)
>>>> +
>>>> +/*
>>>> + * Macros for attribute property of AI engine registers accessed by kernel
>>>> + * 0 - 7 bits: tile type bits
>>>> + * 8 - 15 bits: permission bits. If it is 1, it allows write from userspace
>>>> + */
>>>> +#define AIE_REGS_ATTR_TILE_TYPE_SHIFT  0U
>>>> +#define AIE_REGS_ATTR_PERM_SHIFT       8U
>>>> +#define AIE_REGS_ATTR_TILE_TYPE_MASK   GENMASK(AIE_REGS_ATTR_PERM_SHIFT - 1,
>>>> \
>>>> +                                               AIE_REGS_ATTR_TILE_TYPE_SHIFT)
>>>> +#define AIE_REGS_ATTR_PERM_MASK                GENMASK(15, \
>>>> +                            ��                  AIE_REGS_ATTR_PERM_SHIFT)
>>>> +
>>>> +/**
>>>> + * struct aie_tile_regs - contiguous range of AI engine register
>>>> + *                       within an AI engine tile
>>>> + * @soff: start offset of the range
>>>> + * @eoff: end offset of the range
>>>> + * @attribute: registers attribute. It uses AIE_REGS_ATTR_* macros defined
>>>> + *            above.
>>>> + */
>>>> +struct aie_tile_regs {
>>>> +       size_t soff;
>>>> +       size_t eoff;
>>>> +       u32 attribute;
>>>> +};
>>>> +
>>>> +struct aie_device;
>>>> +struct aie_partition;
>>>> +
>>>> +/**
>>>> + * struct aie_tile_operations - AI engine device operations
>>>> + * @get_tile_type: get type of tile based on tile operation
>>>> + *
>>>> + * Different AI engine device version has its own device
>>>> + * operation.
>>>> + */
>>>> +struct aie_tile_operations {
>>>> +       u32 (*get_tile_type)(struct aie_location *loc);
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct aie_resource - AI engine resource structure
>>>> + * @bitmap: resource bitmap
>>>> + * @total: total number of resource
>>>> + */
>>>> +struct aie_resource {
>>>> +       unsigned long *bitmap;
>>>> +       u32 total;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct aie_device - AI engine device structure
>>>> + * @partitions: list of partitions requested
>>>> + * @cdev: cdev for the AI engine
>>>> + * @dev: device for the AI engine device
>>>> + * @mlock: protection for AI engine device operations
>>>> + * @base: AI engine device base virtual address
>>>> + * @res: memory resource of AI engine device
>>>> + * @kernel_regs: array of kernel only registers
>>>> + * @ops: tile operations
>>>> + * @size: size of the AI engine address space
>>>> + * @array_shift: array address shift
>>>> + * @col_shift: column address shift
>>>> + * @row_shift: row address shift
>>>> + * @cols_res: AI engine columns resources to indicate
>>>> + *           while columns are occupied by partitions.
>>>> + * @num_kernel_regs: number of kernel only registers range
>>>> + * @version: AI engine device version
>>>> + */
>>>> +struct aie_device {
>>>> +       struct list_head partitions;
>>>> +       struct cdev cdev;
>>>> +       struct device dev;
>>>> +       struct mutex mlock; /* protection for AI engine partitions */
>>>> +       void __iomem *base;
>>>> +       struct resource *res;
>>>> +       const struct aie_tile_regs *kernel_regs;
>>>> +       const struct aie_tile_operations *ops;
>>>> +       size_t size;
>>>> +       struct aie_resource cols_res;
>>>> +       u32 array_shift;
>>>> +       u32 col_shift;
>>>> +       u32 row_shift;
>>>> +       u32 num_kernel_regs;
>>>> +       int version;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct aie_partition - AI engine partition structure
>>>> + * @node: list node
>>>> + * @adev: pointer to AI device instance
>>>> + * @range: range of partition
>>>> + * @mlock: protection for AI engine partition operations
>>>> + * @dev: device for the AI engine partition
>>>> + * @partition_id: partition id. Partition ID is the identifier
>>>> + *               of the AI engine partition in the system.
>>>> + * @status: indicate if the partition is in use
>>>> + */
>>>> +struct aie_partition {
>>>> +       struct list_head node;
>>>> +       struct aie_device *adev;
>>>> +       struct aie_range range;
>>>> +       struct mutex mlock; /* protection for AI engine partition operations
>>>> */
>>>> +       struct device dev;
>>>> +       u32 partition_id;
>>>> +       u32 status;
>>>> +};
>>>> +
>>>> +extern struct class *aie_class;
>>>> +extern const struct file_operations aie_part_fops;
>>>> +
>>>> +#define cdev_to_aiedev(i_cdev) container_of((i_cdev), struct aie_device,
>>>> cdev)
>>>> +#define dev_to_aiedev(_dev) container_of((_dev), struct aie_device, dev)
>>>> +#define dev_to_aiepart(_dev) container_of((_dev), struct aie_partition, dev)
>>>> +
>>>> +#define aie_col_mask(adev) ({ \
>>>> +       struct aie_device *_adev = (adev); \
>>>> +       GENMASK_ULL(_adev->array_shift - 1, _adev->col_shift);  \
>>>> +       })
>>>> +
>>>> +#define aie_row_mask(adev) ({ \
>>>> +       struct aie_device *_adev = (adev); \
>>>> +       GENMASK_ULL(_adev->col_shift - 1, _adev->row_shift);  \
>>>> +       })
>>>> +
>>>> +#define aie_tile_reg_mask(adev) ({ \
>>>> +       struct aie_device *_adev = (adev); \
>>>> +       GENMASK_ULL(_adev->row_shift - 1, 0);  \
>>>> +       })
>>>> +
>>>> +/*
>>>> + * Need to define field get, as AI engine shift mask is not constant.
>>>> + * Cannot use FIELD_GET()
>>>> + */
>>>> +#define aie_tile_reg_field_get(mask, shift, regoff) ( \
>>>> +       ((regoff) & (mask)) >> (shift))
>>>> +
>>>> +#define aie_cal_tile_reg(adev, regoff) ( \
>>>> +       aie_tile_reg_field_get(aie_tile_reg_mask(adev), 0, regoff))
>>>> +
>>>> +/**
>>>> + * aie_cal_regoff() - calculate register offset to the whole AI engine
>>>> + *                   device start address
>>>> + * @adev: AI engine device
>>>> + * @loc: AI engine tile location
>>>> + * @regoff_intile: register offset within a tile
>>>> + * @return: register offset to the whole AI engine device start address
>>>> + */
>>>> +static inline u32 aie_cal_regoff(struct aie_device *adev,
>>>> +                                struct aie_location loc, u32 regoff_intile)
>>>> +{
>>>> +       return regoff_intile + (loc.col << adev->col_shift) +
>>>> +              (loc.row << adev->row_shift);
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_validate_location() - validate tile location within an AI engine
>>>> + *                          partition
>>>> + * @apart: AI engine partition
>>>> + * @loc: AI engine tile location
>>>> + * @return: return 0 if it is valid, negative value for errors.
>>>> + *
>>>> + * This function checks if the AI engine location is within the AI engine
>>>> + * partition.
>>>> + */
>>>> +static inline int aie_validate_location(struct aie_partition *apart,
>>>> +                                       struct aie_location loc)
>>>> +{
>>>> +       if (loc.col < apart->range.start.col ||
>>>> +           loc.col >= apart->range.start.col + apart->range.size.col ||
>>>> +           loc.row < apart->range.start.row ||
>>>> +           loc.row >= apart->range.start.row + apart->range.size.row)
>>>> +               return -EINVAL;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +int aie_resource_initialize(struct aie_resource *res, int count);
>>>> +void aie_resource_uninitialize(struct aie_resource *res);
>>>> +int aie_resource_check_region(struct aie_resource *res, u32 start,
>>>> +                             u32 count);
>>>> +int aie_resource_get_region(struct aie_resource *res, u32 start,
>>>> +                           u32 count);
>>>> +void aie_resource_put_region(struct aie_resource *res, int start, u32 count);
>>>> +
>>>> +const struct file_operations *aie_part_get_fops(void);
>>>> +u8 aie_part_in_use(struct aie_partition *apart);
>>>> +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
>>>> +                                               u32 partition_id);
>>>> +struct aie_partition *aie_request_partition(struct aie_device *adev,
>>>> +                                           struct aie_partition_req *req);
>>>> +struct aie_partition *of_aie_part_probe(struct aie_device *adev,
>>>> +                                       struct device_node *nc);
>>>> +void aie_part_remove(struct aie_partition *apart);
>>>> +
>>>> +int aie_device_init(struct aie_device *adev);
>>>> +#endif /* AIE_INTERNAL_H */
>>>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c
>>>> b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
>>>> new file mode 100644
>>>> index 0000000..fc8f9f5
>>>> --- /dev/null
>>>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
>>>> @@ -0,0 +1,498 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Xilinx AI Engine partition driver
>>>> + *
>>>> + * Copyright (C) 2020 Xilinx, Inc.
>>>> + */
>>>> +
>>>> +#include <linux/cdev.h>
>>>> +#include <linux/delay.h>
>>>> +#include <linux/device.h>
>>>> +#include <linux/fs.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/list.h>
>>>> +#include <linux/mm.h>
>>>> +#include <linux/mman.h>
>>>> +#include <linux/mmu_context.h>
>>>> +#include <linux/mutex.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/of_device.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/uaccess.h>
>>>> +#include <linux/uio.h>
>>>> +#include <uapi/linux/xlnx-ai-engine.h>
>>>> +
>>>> +#include "ai-engine-internal.h"
>>>> +
>>>> +/**
>>>> + * aie_cal_loc() - calculate tile location from register offset to the AI
>>>> + *                engine device
>>>> + * @adev: AI engine device
>>>> + * @loc: memory pointer to restore returning location information
>>>> + * @regoff: tile internal register offset
>>>> + *
>>>> + * This function returns the tile location.
>>>> + */
>>>> +static void aie_cal_loc(struct aie_device *adev,
>>>> +                       struct aie_location *loc, u64 regoff)
>>>> +{
>>>> +       loc->col = (u32)aie_tile_reg_field_get(aie_col_mask(adev),
>>>> +                                              adev->col_shift, regoff);
>>>> +       loc->row = (u32)aie_tile_reg_field_get(aie_row_mask(adev),
>>>> +                                              adev->row_shift, regoff);
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_part_reg_validation() - validate AI engine partition register access
>>>> + * @apart: AI engine partition
>>>> + * @offset: AI engine register offset
>>>> + * @len: len of data to write/read
>>>> + * @is_write: is the access to write to register
>>>> + * @return: 0 for success, or negative value for failure.
>>>> + *
>>>> + * This function validate if the register to access is within the AI engine
>>>> + * partition. If it is write access, if the register is writable by user.
>>>> + */
>>>> +static int aie_part_reg_validation(struct aie_partition *apart, size_t
>>>> offset,
>>>> +                                  size_t len, u8 is_write)
>>>> +{
>>>> +       struct aie_device *adev;
>>>> +       u32 regend32, ttype;
>>>> +       u64 regoff, regend64;
>>>> +       struct aie_location loc;
>>>> +       unsigned int i;
>>>> +
>>>> +       adev = apart->adev;
>>>> +       if (offset % sizeof(u32)) {
>>>> +               dev_err(&apart->dev,
>>>> +                       "Invalid reg off(0x%zx), not 32bit aligned.\n",
>>>> +                       offset);
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       if (len % sizeof(u32)) {
>>>> +               dev_err(&apart->dev, "Invalid reg operation len %zu.\n", len);
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       regoff = aie_cal_tile_reg(adev, offset);
>>>> +       regend64 = regoff + len;
>>>> +       if (regend64 >= BIT_ULL(adev->row_shift)) {
>>>> +               dev_err(&apart->dev,
>>>> +                       "Invalid reg operation len %zu.\n", len);
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       aie_cal_loc(adev, &loc, offset);
>>>> +       if (aie_validate_location(apart, loc)) {
>>>> +               dev_err(&apart->dev,
>>>> +                       "Invalid (%d,%d) out of part(%d,%d),(%d,%d)\n",
>>>> +                       loc.col, loc.row,
>>>> +                       apart->range.start.col, apart->range.start.row,
>>>> +                       apart->range.size.col, apart->range.size.row);
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       if (!is_write)
>>>> +               return 0;
>>>> +
>>>> +       regend32 = lower_32_bits(regend64);
>>>> +       ttype = adev->ops->get_tile_type(&loc);
>>>> +       for (i = 0; i < adev->num_kernel_regs; i++) {
>>>> +               const struct aie_tile_regs *regs;
>>>> +               u32 rttype, writable;
>>>> +
>>>> +               regs = &adev->kernel_regs[i];
>>>> +               rttype = (regs->attribute & AIE_REGS_ATTR_TILE_TYPE_MASK) >>
>>>> +                        AIE_REGS_ATTR_TILE_TYPE_SHIFT;
>>>> +               writable = (regs->attribute & AIE_REGS_ATTR_PERM_MASK) >>
>>>> +                          AIE_REGS_ATTR_PERM_SHIFT;
>>>> +               if (!(ttype & rttype))
>>>> +                       continue;
>>>> +               if ((regoff >= regs->soff && regoff <= regs->eoff) ||
>>>> +                   (regend32 >= regs->soff && regend32 <= regs->eoff)) {
>>>> +                       if (!writable) {
>>>> +                               dev_err(&apart->dev,
>>>> +                                       "reg 0x%zx,0x%zx not writable.\n",
>>>> +                                       offset, len);
>>>> +                               return -EINVAL;
>>>> +                       }
>>>> +               }
>>>> +       }
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_part_write_register() - AI engine partition write register
>>>> + * @apart: AI engine partition
>>>> + * @offset: AI engine register offset
>>>> + * @len: len of data to write
>>>> + * @data: data to write
>>>> + * @mask: mask, if it is non 0, it is mask write.
>>>> + * @return: number of bytes write for success, or negative value for failure.
>>>> + *
>>>> + * This function writes data to the specified registers.
>>>> + * If the mask is non 0, it is mask write.
>>>> + */
>>>> +static int aie_part_write_register(struct aie_partition *apart, size_t
>>>> offset,
>>>> +                                  size_t len, void *data, u32 mask)
>>>> +{
>>>> +       int ret;
>>>> +       void __iomem *va;
>>>> +
>>>> +       if (mask && len > sizeof(u32)) {
>>>> +               /* For mask write, only allow 32bit. */
>>>> +               dev_err(&apart->dev,
>>>> +                       "failed mask write, len is more that 32bit.\n");
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       /* offset is expected to be relative to the start of the partition */
>>>> +       offset += aie_cal_regoff(apart->adev, apart->range.start, 0);
>>>> +       ret = aie_part_reg_validation(apart, offset, len, 1);
>>>> +       if (ret < 0) {
>>>> +               dev_err(&apart->dev, "failed to write to 0x%zx,0x%zx.\n",
>>>> +                       offset, len);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       va = apart->adev->base + offset;
>>>> +       if (!mask) {
>>>> +               if (len == sizeof(u32))
>>>> +                       iowrite32(*((u32 *)data),  va);
>>>> +               else
>>>> +                       memcpy_toio(va, data, len);
>>>> +       } else {
>>>> +               u32 val = ioread32(va);
>>>> +
>>>> +               val &= ~mask;
>>>> +               val |= *((u32 *)data) & mask;
>>>> +               iowrite32(val, va);
>>>> +       }
>>>> +
>>>> +       return (int)len;
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_part_access_regs() - AI engine partition registers access
>>>> + * @apart: AI engine partition
>>>> + * @num_reqs: number of access requests
>>>> + * @reqs: array of registers access
>>>> + * @return: 0 for success, and negative value for failure.
>>>> + *
>>>> + * This function executes AI engine partition register access requests.
>>>> + */
>>>> +static int aie_part_access_regs(struct aie_partition *apart, u32 num_reqs,
>>>> +                               struct aie_reg_args *reqs)
>>>> +{
>>>> +       u32 i;
>>>> +
>>>> +       for (i = 0; i < num_reqs; i++) {
>>>> +               struct aie_reg_args *args = &reqs[i];
>>>> +               int ret;
>>>> +
>>>> +               if (args->op != AIE_REG_WRITE) {
>>>> +                       dev_err(&apart->dev,
>>>> +                               "Invalid register command type: %u.\n",
>>>> +                               args->op);
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +               ret = aie_part_write_register(apart,
>>>> +                                             (size_t)args->offset,
>>>> +                                             sizeof(args->val),
>>>> +                                             &args->val, args->mask);
>>>> +               if (ret < 0) {
>>>> +                       dev_err(&apart->dev, "reg op %u failed: 0x%llx.\n",
>>>> +                               args->op, args->offset);
>>>> +                       return ret;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int aie_part_release(struct inode *inode, struct file *filp)
>>>> +{
>>>> +       struct aie_partition *apart = filp->private_data;
>>>> +       int ret;
>>>> +
>>>> +       /*
>>>> +        * TODO: It will need to reset the SHIM columns and gate the
>>>> +        * tiles of the partition.
>>>> +        */
>>>> +       ret = mutex_lock_interruptible(&apart->mlock);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       apart->status = 0;
>>>> +       mutex_unlock(&apart->mlock);
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static const struct vm_operations_struct aie_part_physical_vm_ops = {
>>>> +#ifdef CONFIG_HAVE_IOREMAP_PROT
>>>> +       .access = generic_access_phys,
>>>> +#endif
>>>> +};
>>>> +
>>>> +static int aie_part_mmap(struct file *fp, struct vm_area_struct *vma)
>>>> +{
>>>> +       struct aie_partition *apart = fp->private_data;
>>>> +       struct aie_device *adev = apart->adev;
>>>> +       unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
>>>> +       phys_addr_t addr;
>>>> +       size_t size;
>>>> +
>>>> +       if (vma->vm_end < vma->vm_start)
>>>> +               return -EINVAL;
>>>> +       /* Only allow userspace directly read registers */
>>>> +       if (vma->vm_flags & VM_WRITE) {
>>>> +               dev_err(&apart->dev, "%s: do not support writable mmap.\n",
>>>> +                       __func__);
>>>> +               return -EINVAL;
>>>> +       }
>>>> +       vma->vm_private_data = apart;
>>>> +       vma->vm_ops = &aie_part_physical_vm_ops;
>>>> +       size = apart->range.size.col << adev->col_shift;
>>>> +       if ((vma->vm_end - vma->vm_start) > (size - offset)) {
>>>> +               dev_err(&apart->dev,
>>>> +                       "%s: size exceed.\n", __func__);
>>>> +               return -EINVAL;
>>>> +       }
>>>> +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
>>>> +       /* Calculate the partition address */
>>>> +       addr = adev->res->start;
>>>> +       addr += apart->range.start.col << adev->col_shift;
>>>> +       addr += apart->range.start.row << adev->row_shift;
>>>> +       addr += offset;
>>>> +       return remap_pfn_range(vma,
>>>> +                              vma->vm_start,
>>>> +                              addr >> PAGE_SHIFT,
>>>> +                              vma->vm_end - vma->vm_start,
>>>> +                              vma->vm_page_prot);
>>>> +}
>>>> +
>>>> +static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long
>>>> arg)
>>>> +{
>>>> +       struct aie_partition *apart = fp->private_data;
>>>> +       void __user *argp = (void __user *)arg;
>>>> +       long ret;
>>>> +
>>>> +       switch (cmd) {
>>>> +       case AIE_REG_IOCTL:
>>>> +       {
>>>> +               struct aie_reg_args raccess;
>>>> +
>>>> +               if (copy_from_user(&raccess, argp, sizeof(raccess)))
>>>> +                       return -EFAULT;
>>>> +
>>>> +               ret = mutex_lock_interruptible(&apart->mlock);
>>>> +               if (ret)
>>>> +                       return ret;
>>>> +
>>>> +               ret = aie_part_access_regs(apart, 1, &raccess);
>>>> +               mutex_unlock(&apart->mlock);
>>>> +               break;
>>>> +       }
>>>> +       default:
>>>> +               dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd);
>>>> +               ret = -EINVAL;
>>>> +               break;
>>>> +       }
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +const struct file_operations aie_part_fops = {
>>>> +       .owner          = THIS_MODULE,
>>>> +       .release        = aie_part_release,
>>>> +       .mmap           = aie_part_mmap,
>>>> +       .unlocked_ioctl = aie_part_ioctl,
>>>> +};
>>>> +
>>>> +/**
>>>> + * aie_part_release_device() - release an AI engine partition instance
>>>> + * @dev: AI engine partition device
>>>> + *
>>>> + * It will be called by device driver core when no one holds a valid
>>>> + * pointer to @dev anymore.
>>>> + */
>>>> +static void aie_part_release_device(struct device *dev)
>>>> +{
>>>> +       struct aie_partition *apart = dev_to_aiepart(dev);
>>>> +       struct aie_device *adev = apart->adev;
>>>> +       int ret;
>>>> +
>>>> +       ret = mutex_lock_interruptible(&adev->mlock);
>>>> +       if (ret) {
>>>> +               dev_warn(&apart->dev,
>>>> +                        "getting adev->mlock is interrupted by signal\n");
>>>> +       }
>>>> +
>>>> +       aie_resource_put_region(&adev->cols_res, apart->range.start.col,
>>>> +                               apart->range.size.col);
>>>> +       list_del(&apart->node);
>>>> +       mutex_unlock(&adev->mlock);
>>>> +       put_device(apart->dev.parent);
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_create_partition() - create AI engine partition instance
>>>> + * @adev: AI engine device
>>>> + * @range: AI engine partition range to check. A range describes a group
>>>> + *        of AI engine tiles.
>>>> + * @return: created AI engine partition pointer for success, and PTR_ERR
>>>> + *         for failure.
>>>> + *
>>>> + * This function creates an AI engine partition instance.
>>>> + * It creates AI engine partition, the AI engine partition device and
>>>> + * the AI engine partition character device.
>>>> + */
>>>> +static struct aie_partition *aie_create_partition(struct aie_device *adev,
>>>> +                                                 struct aie_range *range)
>>>> +{
>>>> +       struct aie_partition *apart;
>>>> +       struct device *dev;
>>>> +       char devname[32];
>>>> +       int ret;
>>>> +
>>>> +       ret = mutex_lock_interruptible(&adev->mlock);
>>>> +       if (ret)
>>>> +               return ERR_PTR(ret);
>>>> +
>>>> +       ret = aie_resource_check_region(&adev->cols_res, range->start.col,
>>>> +                                       range->size.col);
>>>> +       if (ret != range->start.col) {
>>>> +               dev_err(&adev->dev, "invalid partition (%u,%u)(%u,%u).\n",
>>>> +                       range->start.col, range->start.row,
>>>> +                       range->size.col, range->size.row);
>>>> +               mutex_unlock(&adev->mlock);
>>>> +               return ERR_PTR(-EINVAL);
>>>> +       }
>>>> +       ret = aie_resource_get_region(&adev->cols_res, range->start.col,
>>>> +                                     range->size.col);
>>>> +       if (ret != range->start.col) {
>>>> +               dev_err(&adev->dev, "failed to get partition
>>>> (%u,%u)(%u,%u).\n",
>>>> +                       range->start.col, range->start.row,
>>>> +                       range->size.col, range->size.row);
>>>> +               mutex_unlock(&adev->mlock);
>>>> +               return ERR_PTR(-EFAULT);
>>>> +       }
>>>> +       mutex_unlock(&adev->mlock);
>>>> +
>>>> +       apart = devm_kzalloc(&adev->dev, sizeof(*apart), GFP_KERNEL);
>>>> +       if (!apart)
>>>> +               return ERR_PTR(-ENOMEM);
>>>> +
>>>> +       apart->adev = adev;
>>>> +       memcpy(&apart->range, range, sizeof(*range));
>>>> +       mutex_init(&apart->mlock);
>>>> +
>>>> +       /* Create AI engine partition device */
>>>> +       dev = &apart->dev;
>>>> +       device_initialize(dev);
>>>> +       dev->parent = &adev->dev;
>>>> +       dev->class = aie_class;
>>>> +       dev_set_drvdata(dev, apart);
>>>> +       snprintf(devname, sizeof(devname) - 1, "aiepart_%d_%d",
>>>> +                apart->range.start.col, apart->range.size.col);
>>>> +       dev_set_name(dev, devname);
>>>> +       /* We can now rely on the release function for cleanup */
>>>> +       dev->release = aie_part_release_device;
>>>> +       ret = device_add(dev);
>>>> +       if (ret) {
>>>> +               dev_err(dev, "device_add failed: %d\n", ret);
>>>> +               put_device(dev);
>>>> +               return ERR_PTR(ret);
>>>> +       }
>>>> +
>>>> +       ret = mutex_lock_interruptible(&adev->mlock);
>>>> +       if (ret) {
>>>> +               put_device(dev);
>>>> +               return ERR_PTR(ret);
>>>> +       }
>>>> +
>>>> +       list_add_tail(&apart->node, &adev->partitions);
>>>> +       mutex_unlock(&adev->mlock);
>>>> +       get_device(&adev->dev);
>>>> +       dev_dbg(dev, "created AIE partition device.\n");
>>>> +
>>>> +       return apart;
>>>> +}
>>>> +
>>>> +struct aie_partition *
>>>> +of_aie_part_probe(struct aie_device *adev, struct device_node *nc)
>>>> +{
>>>> +       struct aie_partition *apart;
>>>> +       struct aie_range range;
>>>> +       u32 partition_id, regs[4];
>>>> +       int ret;
>>>> +
>>>> +       /* Select device driver */
>>>> +       ret = of_property_read_u32_array(nc, "reg", regs, ARRAY_SIZE(regs));
>>>> +       if (ret < 0) {
>>>> +               dev_err(&adev->dev,
>>>> +                       "probe %pOF failed, no tiles range information.\n",
>>>> +                       nc);
>>>> +               return ERR_PTR(ret);
>>>> +       }
>>>> +       range.start.col = regs[0];
>>>> +       range.start.row = regs[1];
>>>> +       range.size.col = regs[2];
>>>> +       range.size.row = regs[3];
>>>> +
>>>> +       ret = of_property_read_u32_index(nc, "xlnx,partition-id", 0,
>>>> +                                        &partition_id);
>>>> +       if (ret < 0) {
>>>> +               dev_err(&adev->dev,
>>>> +                       "probe %pOF failed, no partition id.\n", nc);
>>>> +               return ERR_PTR(ret);
>>>> +       }
>>>> +
>>>> +       ret = mutex_lock_interruptible(&adev->mlock);
>>>> +       if (ret)
>>>> +               return ERR_PTR(ret);
>>>> +
>>>> +       apart = aie_get_partition_from_id(adev, partition_id);
>>>> +       mutex_unlock(&adev->mlock);
>>>> +       if (apart) {
>>>> +               dev_err(&adev->dev,
>>>> +                       "probe failed: partition %u exists.\n",
>>>> +                       partition_id);
>>>> +               return ERR_PTR(ret);
>>>> +       }
>>>> +
>>>> +       apart = aie_create_partition(adev, &range);
>>>> +       if (IS_ERR(apart)) {
>>>> +               dev_err(&adev->dev,
>>>> +                       "%s: failed to create part(%u,%u),(%u,%u).\n",
>>>> +                       __func__, range.start.col, range.start.row,
>>>> +                       range.size.col, range.size.row);
>>>> +               return apart;
>>>> +       }
>>>> +
>>>> +       of_node_get(nc);
>>>> +       apart->dev.of_node = nc;
>>>> +       apart->partition_id = partition_id;
>>>> +
>>>> +       dev_info(&adev->dev,
>>>> +                "AI engine part(%u,%u),(%u,%u), id %u is probed
>>>> successfully.\n",
>>>> +                range.start.col, range.start.row,
>>>> +                range.size.col, range.size.row, apart->partition_id);
>>>> +
>>>> +       return apart;
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_destroy_part() - destroy AI engine partition
>>>> + * @apart: AI engine partition
>>>> + *
>>>> + * This function will remove AI engine partition.
>>>> + */
>>>> +void aie_part_remove(struct aie_partition *apart)
>>>> +{
>>>> +       device_del(&apart->dev);
>>>> +       put_device(&apart->dev);
>>>> +}
>>>> diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-res.c
>>>> b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
>>>> new file mode 100644
>>>> index 0000000..36f08bf
>>>> --- /dev/null
>>>> +++ b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
>>>> @@ -0,0 +1,114 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Xilinx AI Engine device driver
>>>> + *
>>>> + * Copyright (C) 2020 Xilinx, Inc.
>>>> + */
>>>> +
>>>> +#include <linux/bitmap.h>
>>>> +
>>>> +#include "ai-engine-internal.h"
>>>> +
>>>> +/**
>>>> + * aie_resource_initialize() - initialize AI engine resource
>>>> + * @res: pointer to AI engine resource
>>>> + * @count: total number of element of this resource
>>>> + * @return: 0 for success, negative value for failure.
>>>> + *
>>>> + * This function will initialize the data structure for the
>>>> + * resource.
>>>> + */
>>>> +int aie_resource_initialize(struct aie_resource *res, int count)
>>>> +{
>>>> +       if (!res || !count)
>>>> +               return -EINVAL;
>>>> +       res->bitmap = bitmap_zalloc(count, GFP_KERNEL);
>>>> +       if (!res->bitmap)
>>>> +               return -ENOMEM;
>>>> +       res->total = count;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_resource_uninitialize() - uninitialize AI engine resource
>>>> + * @res: pointer to AI engine resource
>>>> + *
>>>> + * This function will release the AI engine resource data members.
>>>> + */
>>>> +void aie_resource_uninitialize(struct aie_resource *res)
>>>> +{
>>>> +       res->total = 0;
>>>> +       if (res->bitmap)
>>>> +               bitmap_free(res->bitmap);
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_resource_check() - check availability of requested resource
>>>> + * @res: pointer to AI engine resource to check
>>>> + * @start: start index of the required resource, it will only be used if
>>>> + *        @continuous is 1. It will check the available resource starting
>>>> from
>>>> + *        @start
>>>> + * @count: number of requested element
>>>> + * @return: start resource id if the requested number of resources are
>>>> available
>>>> + *         It will return negative value of errors.
>>>> + *
>>>> + * This function will check the availability. It will return start resource
>>>> id
>>>> + * if the requested number of resources are available.
>>>> + */
>>>> +int aie_resource_check_region(struct aie_resource *res,
>>>> +                             u32 start, u32 count)
>>>> +{
>>>> +       unsigned long id;
>>>> +
>>>> +       if (!res || !res->bitmap || !count)
>>>> +               return -EINVAL;
>>>> +       id = bitmap_find_next_zero_area(res->bitmap, res->total, start,
>>>> +                                       count, 0);
>>>> +       if (id >= res->total)
>>>> +               return -ERANGE;
>>>> +
>>>> +       return (int)id;
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_resource_get_region() - get requested AI engine resource
>>>> + * @res: pointer to AI engine resource to check
>>>> + * @count: number of requested element
>>>> + * @start: start index of the required resource
>>>> + * @return: start resource id for success, and negative value for failure.
>>>> + *
>>>> + * This function check if the requested AI engine resource is available.
>>>> + * If it is available, mark it used and return the start resource id.
>>>> + */
>>>> +int aie_resource_get_region(struct aie_resource *res, u32 start, u32 count)
>>>> +{
>>>> +       unsigned long off;
>>>> +
>>>> +       if (!res || !res->bitmap || !count)
>>>> +               return -EINVAL;
>>>> +       off = bitmap_find_next_zero_area(res->bitmap, res->total, start,
>>>> +                                        count, 0);
>>>> +       if (off >= res->total) {
>>>> +               pr_err("Failed to get available AI engine resource.\n");
>>>> +               return -ERANGE;
>>>> +       }
>>>> +       bitmap_set(res->bitmap, off, count);
>>>> +
>>>> +       return (int)off;
>>>> +}
>>>> +
>>>> +/**
>>>> + * aie_resource_put_region() - release requested AI engine resource
>>>> + * @res: pointer to AI engine resource to check
>>>> + * @start: start index of the resource to release
>>>> + * @count: number of elements to release
>>>> + *
>>>> + * This function release the requested AI engine resource.
>>>> + */
>>>> +void aie_resource_put_region(struct aie_resource *res, int start, u32 count)
>>>> +{
>>>> +       if (!res || !count)
>>>> +               return;
>>>> +       bitmap_clear(res->bitmap, start, count);
>>>> +}
>>>> diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-
>>>> engine.h
>>>> new file mode 100644
>>>> index 0000000..acbc781
>>>> --- /dev/null
>>>> +++ b/include/uapi/linux/xlnx-ai-engine.h
>>>> @@ -0,0 +1,107 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>>>> +/*
>>>> + * Copyright (c) 2020, Xilinx Inc.
>>>> + */
>>>> +
>>>> +#ifndef _UAPI_AI_ENGINE_H_
>>>> +#define _UAPI_AI_ENGINE_H_
>>>> +
>>>> +#include <linux/ioctl.h>
>>>> +#include <linux/types.h>
>>>> +
>>>> +enum aie_reg_op {
>>>> +       AIE_REG_WRITE,
>>>> +};
>>>> +
>>>> +/* AI engine partition is in use */
>>>> +#define XAIE_PART_STATUS_INUSE         (1U << 0)
>>>> +
>>>> +/**
>>>> + * struct aie_location - AIE location information
>>>> + * @col: column id
>>>> + * @row: row id
>>>> + */
>>>> +struct aie_location {
>>>> +       __u32 col;
>>>> +       __u32 row;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct aie_range - AIE range information
>>>> + * @start: start tile location
>>>> + * @size: size of the range, number of columns and rows
>>>> + */
>>>> +struct aie_range {
>>>> +       struct aie_location start;
>>>> +       struct aie_location size;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct aie_reg_args - AIE access register arguments
>>>> + * @op: if this request is to read, write or poll register
>>>> + * @mask: mask for mask write, 0 for not mask write
>>>> + * @offset: offset of register to the start of an AI engine partition
>>>> + * @val: value to write or get
>>>> + */
>>>> +struct aie_reg_args {
>>>> +       enum aie_reg_op op;
>>>> +       __u32 mask;
>>>> +       __u64 offset;
>>>> +       __u32 val;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct aie_range_args - AIE range request arguments
>>>> + * @partition_id: partition id. It is used to identify the
>>>> + *               AI engine partition in the system.
>>>> + * @uid: image identifier loaded on the AI engine partition
>>>> + * @range: range of AIE tiles
>>>> + * @status: indicate if the AI engine is in use.
>>>> + *         0 means not in used, otherwise, in use.
>>>> + */
>>>> +struct aie_range_args {
>>>> +       __u32 partition_id;
>>>> +       __u32 uid;
>>>> +       struct aie_range range;
>>>> +       __u32 status;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct aie_partition_query - AIE partition query arguments
>>>> + * @partition_cnt: number of defined partitions in the system
>>>> + * @partitions: buffer to store defined partitions information.
>>>> + */
>>>> +struct aie_partition_query {
>>>> +       struct aie_range_args *partitions;
>>>> +       __u32 partition_cnt;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct aie_partition_req - AIE request partition arguments
>>>> + * @partition_id: partition node id. It is used to identify the AI engine
>>>> + *               partition in the system.
>>>> + * @uid: image identifier loaded on the AI engine partition
>>>> + * @meta_data: meta data to indicate which resources used by application.
>>>> + * @flag: used for application to indicate particular driver requirements
>>>> + *       application wants to have for the partition. e.g. do not clean
>>>> + *       resource when closing the partition.
>>>> + */
>>>> +struct aie_partition_req {
>>>> +       __u32 partition_id;
>>>> +       __u32 uid;
>>>> +       __u64 meta_data;
>>>> +       __u32 flag;
>>>> +};
>>>> +
>>>> +#define AIE_IOCTL_BASE 'A'
>>>> +
>>>> +/* AI engine device IOCTL operations */
>>>> +#define AIE_ENQUIRE_PART_IOCTL         _IOWR(AIE_IOCTL_BASE, 0x1, \
>>>> +                                             struct aie_partition_query)
>>>> +#define AIE_REQUEST_PART_IOCTL         _IOR(AIE_IOCTL_BASE, 0x2, \
>>>> +                                            struct aie_partition_req)
>>>> +
>>>> +/* AI engine partition IOCTL operations */
>>>> +#define AIE_REG_IOCTL                  _IOWR(AIE_IOCTL_BASE, 0x8, \
>>>> +                                             struct aie_reg_args)
>>>> +#endif
>> _______________________________________________
>> dri-devel mailing list
>> [email protected]
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel

2020-12-14 10:51:46

by Daniel Vetter

[permalink] [raw]
Subject: Re: [PATCH 2/9] misc: Add Xilinx AI engine device driver

On Fri, Dec 11, 2020 at 06:27:05PM -0800, Jiaying Liang wrote:
>
> On 12/9/20 4:47 AM, Daniel Vetter wrote:
> > On Tue, Dec 08, 2020 at 11:54:57AM -0800, Jiaying Liang wrote:
> > > On 12/8/20 9:12 AM, Nicolas Dufresne wrote:
> > > > Le mercredi 18 novembre 2020 à 00:06 -0800, Wendy Liang a écrit :
> > > > > Create AI engine device/partition hierarchical structure.
> > > > >
> > > > > Each AI engine device can have multiple logical partitions(groups of AI
> > > > > engine tiles). Each partition is column based and has its own node ID
> > > > > in the system. AI engine device driver manages its partitions.
> > > > >
> > > > > Applications can access AI engine partition through the AI engine
> > > > > partition driver instance. AI engine registers write is moved to kernel
> > > > > as there are registers in the AI engine array needs privilege
> > > > > permission.
> > > > Hi there, it's nice to see an effort to upstream an AI driver. I'm a little
> > > > worried this driver is not obvious to use from it's source code itself. So you
> > > > have reference to some Open Source code that demonstrate it's usage ?
> > > We have AI engine library which provides a cross platforms APIs for other
> > >
> > > libraries/application to use the hardware. Here is the source code:
> > >
> > > https://github.com/Xilinx/embeddedsw/tree/master/XilinxProcessorIPLib/drivers/aienginev2/src
> > >
> > > The cross platforms AI engine library runs in LInux userspace it defines how
> > > to
> > >
> > > configure, and the kernel driver controls if what can be access and manage
> > > errors from device.
> > So I kinda ignored this driver submission because in the past all these AI
> > drivers had at best incomplete open source (usually the compiler is
> > closed, often also large parts of the runtime). I think yours would be the
> > first that breaks this trend, is that really the case? I.e. I could make
> > full use of this hw without any closed source bits to run DNN workloads
> > and things like that?
> AI engine can be used for signaling processing or high performance computing
>
> the kernel driver works on the AI engine software library which I mentioned
> above,
>
> which will be used by Xilinx runtime:
> https://xilinx.github.io/XRT/2020.2/html/index.html
>
> Xilinx runtime is a layer for acceleration libraries or applications to use
> Xilinx accelerators.

Hm maybe I'm mixing things up, but it feels like there was a discussion
about the xilinx runtime in the past with a drm driver even. The hangup
was that the runtime is all open, but the offline compiler isn't. Is that
still the case, or am I mixing things up here a bit?

> e.g. it has OpenCL implementation
> > If that's the case then I think there's nothing stopping us from doing the
> > right thing and merging this driver into the right subsystem: The
> > subsystem for accelerators which their own memory and who want dma-buf
> > integration is drivers/gpu, not drivers/misc.
>
> The AI engine kernel driver is used for device runtime configuration update,
>
> and runtime monitoring, such as async errors detection. The buffer
> management
>
> is out of the AI engine driver, but it is covered by Xilinx runtime:
>
> https://github.com/Xilinx/XRT/tree/master/src/runtime_src/core/edge/drm/zocl
>
> AI engine driver imports the DMA buf.
>
>
> The AI engine device is quite different to the GPU devices. The AI engine
>
> operations are still needs driver specific ioctls.
>
> We have more than 100 cores tiles, each tiles functions can be defined at
> compilation
>
> time, at runtime, we load the configuration (application defined I/O
> commands) to
>
> configure each tiles registers to set up routing, set up DMAs, configure
> local memories,
>
> and enable the tiles.

Setting up DMA does sound like (buffer) memory management to me. Note that
the memory management that the kernel does does not necessarily translate
directly to client api buffers like opencl has. At least for gpu drivers
you have memory management in both the kernel and userspace.

> As the AI engine device hardware is different to the GPUs,
>
> we are not able to make use of functions abstracted for GPUs, and we don't
> manage the
>
> buffers in this driver. I am not sure if it is ok to add the driver to
> drivers/gpu but not
>
> using abstractions from the GPU abstraction.
>
>
> There is another reply to the patch series to ask for clarification on the
> overview of the
>
> driver, and I had some discussions with other team members. I will reply to
> that email
>
> to provide more details on overall how this driver is used.

Yeah I think that should help with understanding what's the best option
here.
-Daniel

>
> Any suggestions on how to fit the driver in the drivers/gpu or other drivers
> framework
>
> will be much appreciated.
>
>
> Thanks,
>
> Wendy
>
> > Apologies that I'm jumping with the really big arch review when v3 is
> > already on the list. But last few times merging AI drivers to drivers/misc
> > was really just a way to avoid the merge criteria for drivers/gpu
> > acceleration drivers. I'd love to land the first real open AI driver in
> > upstream, properly.
> >
> > Cheers, Daniel
> >
> >
> >
> > > Best Regards,
> > >
> > > Wendy
> > >
> > >
> > > > > Signed-off-by: Wendy Liang<[email protected]>
> > > > > Signed-off-by: Hyun Kwon<[email protected]>
> > > > > ---
> > > > >  MAINTAINERS                                        |   8 +
> > > > >  drivers/misc/Kconfig                               |  12 +
> > > > >  drivers/misc/Makefile                              |   1 +
> > > > >  drivers/misc/xilinx-ai-engine/Makefile             |  11 +
> > > > >  drivers/misc/xilinx-ai-engine/ai-engine-aie.c      | 115 +++++
> > > > >  drivers/misc/xilinx-ai-engine/ai-engine-dev.c      | 448 ++++++++++++++++++
> > > > >  drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 226 ++++++++++
> > > > >  drivers/misc/xilinx-ai-engine/ai-engine-part.c     | 498
> > > > > +++++++++++++++++++++
> > > > >  drivers/misc/xilinx-ai-engine/ai-engine-res.c      | 114 +++++
> > > > >  include/uapi/linux/xlnx-ai-engine.h                | 107 +++++
> > > > >  10 files changed, 1540 insertions(+)
> > > > >  create mode 100644 drivers/misc/xilinx-ai-engine/Makefile
> > > > >  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-aie.c
> > > > >  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dev.c
> > > > >  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-internal.h
> > > > >  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-part.c
> > > > >  create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-res.c
> > > > >  create mode 100644 include/uapi/linux/xlnx-ai-engine.h
> > > > >
> > > > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > > > index 5cc595a..40e3351 100644
> > > > > --- a/MAINTAINERS
> > > > > +++ b/MAINTAINERS
> > > > > @@ -19283,6 +19283,14 @@ T:     githttps://github.com/Xilinx/linux-xlnx.git
> > > > >  F:     Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml
> > > > >  F:     drivers/phy/xilinx/phy-zynqmp.c
> > > > > +XILINX AI ENGINE DRIVER
> > > > > +M:     Wendy Liang<[email protected]>
> > > > > +S:     Supported
> > > > > +F:     Documentation/devicetree/bindings/soc/xilinx/xlnx,ai-engine.yaml
> > > > > +F:     drivers/misc/xilinx-ai-engine/
> > > > > +F:     include/linux/xlnx-ai-engine.h
> > > > > +F:     include/uapi/linux/xlnx-ai-engine.h
> > > > > +
> > > > >  XILLYBUS DRIVER
> > > > >  M:     Eli Billauer<[email protected]>
> > > > >  L:[email protected]
> > > > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > > > > index fafa8b0..0b8ce4d 100644
> > > > > --- a/drivers/misc/Kconfig
> > > > > +++ b/drivers/misc/Kconfig
> > > > > @@ -444,6 +444,18 @@ config XILINX_SDFEC
> > > > >           If unsure, say N.
> > > > > +config XILINX_AIE
> > > > > +       tristate "Xilinx AI engine"
> > > > > +       depends on ARM64 || COMPILE_TEST
> > > > > +       help
> > > > > +         This option enables support for the Xilinx AI engine driver.
> > > > > +         One Xilinx AI engine device can have multiple partitions (groups of
> > > > > +         AI engine tiles). Xilinx AI engine device driver instance manages
> > > > > +         AI engine partitions. User application access its partitions through
> > > > > +         AI engine partition instance file operations.
> > > > > +
> > > > > +         If unsure, say N
> > > > > +
> > > > >  config MISC_RTSX
> > > > >         tristate
> > > > >         default MISC_RTSX_PCI || MISC_RTSX_USB
> > > > > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> > > > > index d23231e..2176b18 100644
> > > > > --- a/drivers/misc/Makefile
> > > > > +++ b/drivers/misc/Makefile
> > > > > @@ -57,3 +57,4 @@ obj-$(CONFIG_HABANA_AI)               += habanalabs/
> > > > >  obj-$(CONFIG_UACCE)            += uacce/
> > > > >  obj-$(CONFIG_XILINX_SDFEC)     += xilinx_sdfec.o
> > > > >  obj-$(CONFIG_HISI_HIKEY_USB)   += hisi_hikey_usb.o
> > > > > +obj-$(CONFIG_XILINX_AIE)       += xilinx-ai-engine/
> > > > > diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-
> > > > > engine/Makefile
> > > > > new file mode 100644
> > > > > index 0000000..7827a0a
> > > > > --- /dev/null
> > > > > +++ b/drivers/misc/xilinx-ai-engine/Makefile
> > > > > @@ -0,0 +1,11 @@
> > > > > +# SPDX-License-Identifier: GPL-2.0-only
> > > > > +#
> > > > > +# Makefile for Xilinx AI engine device driver
> > > > > +#
> > > > > +
> > > > > +obj-$(CONFIG_XILINX_AIE)       += xilinx-aie.o
> > > > > +
> > > > > +xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \
> > > > > +                                  ai-engine-dev.o \
> > > > > +                                  ai-engine-part.o \
> > > > > +                                  ai-engine-res.o
> > > > > diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
> > > > > b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
> > > > > new file mode 100644
> > > > > index 0000000..319260f
> > > > > --- /dev/null
> > > > > +++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c
> > > > > @@ -0,0 +1,115 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > +/*
> > > > > + * Xilinx AI Engine driver AIE device specific implementation
> > > > > + *
> > > > > + * Copyright (C) 2020 Xilinx, Inc.
> > > > > + */
> > > > > +
> > > > > +#include <linux/slab.h>
> > > > > +
> > > > > +#include "ai-engine-internal.h"
> > > > > +
> > > > > +#define AIE_ARRAY_SHIFT                30U
> > > > > +#define AIE_COL_SHIFT          23U
> > > > > +#define AIE_ROW_SHIFT          18U
> > > > > +
> > > > > +/*
> > > > > + * Registers offsets
> > > > > + */
> > > > > +#define AIE_SHIMNOC_L2INTR_MASK_REGOFF         0x00015000U
> > > > > +#define AIE_SHIMNOC_L2INTR_INTR_REGOFF         0x00015010U
> > > > > +#define AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF     0x0001d000U
> > > > > +#define AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF     0x0001d13cU
> > > > > +#define AIE_SHIMNOC_AXIMM_REGOFF               0x0001e020U
> > > > > +#define AIE_SHIMPL_L1INTR_MASK_A_REGOFF                0x00035000U
> > > > > +#define AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF 0x00035050U
> > > > > +#define AIE_SHIMPL_CLKCNTR_REGOFF              0x00036040U
> > > > > +#define AIE_SHIMPL_RESET_REGOFF                        0x0003604cU
> > > > > +#define AIE_TILE_CORE_CLKCNTR_REGOFF           0x00036040U
> > > > > +
> > > > > +static const struct aie_tile_regs aie_kernel_regs[] = {
> > > > > +       /* SHIM AXI MM Config */
> > > > > +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > > > +        .soff = AIE_SHIMNOC_AXIMM_REGOFF,
> > > > > +        .eoff = AIE_SHIMNOC_AXIMM_REGOFF,
> > > > > +       },
> > > > > +       /* SHIM DMA ADDRESS range */
> > > > > +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > > > +        .soff = AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF,
> > > > > +        .eoff = AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF,
> > > > > +       },
> > > > > +       /* SHIM 2nd level interrupt controller */
> > > > > +       {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > > > +        .soff = AIE_SHIMNOC_L2INTR_MASK_REGOFF,
> > > > > +        .eoff = AIE_SHIMNOC_L2INTR_INTR_REGOFF,
> > > > > +       },
> > > > > +       /* SHIM 1st level interrupt controller */
> > > > > +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
> > > > > +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > > > +        .soff = AIE_SHIMPL_L1INTR_MASK_A_REGOFF,
> > > > > +        .eoff = AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF,
> > > > > +       },
> > > > > +       /* SHIM reset Enable */
> > > > > +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
> > > > > +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > > > +        .soff = AIE_SHIMPL_RESET_REGOFF,
> > > > > +        .eoff = AIE_SHIMPL_RESET_REGOFF,
> > > > > +       },
> > > > > +       /* SHIM clock control */
> > > > > +       {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
> > > > > +                     AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > > > +        .soff = AIE_SHIMPL_CLKCNTR_REGOFF,
> > > > > +        .eoff = AIE_SHIMPL_CLKCNTR_REGOFF,
> > > > > +       },
> > > > > +       /* Tile clock control */
> > > > > +       {.attribute = AIE_TILE_TYPE_TILE << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
> > > > > +        .soff = AIE_TILE_CORE_CLKCNTR_REGOFF,
> > > > > +        .eoff = AIE_TILE_CORE_CLKCNTR_REGOFF,
> > > > > +       },
> > > > > +};
> > > > > +
> > > > > +static u32 aie_get_tile_type(struct aie_location *loc)
> > > > > +{
> > > > > +       if (loc->row)
> > > > > +               return AIE_TILE_TYPE_TILE;
> > > > > +       /* SHIM row */
> > > > > +       if ((loc->col % 4) < 2)
> > > > > +               return AIE_TILE_TYPE_SHIMPL;
> > > > > +
> > > > > +       return AIE_TILE_TYPE_SHIMNOC;
> > > > > +}
> > > > > +
> > > > > +static const struct aie_tile_operations aie_ops = {
> > > > > +       .get_tile_type = aie_get_tile_type,
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * aie_device_init() - Initialize AI engine device struct AIE specific
> > > > > + *                    properties
> > > > > + * @adev: AI engine device
> > > > > + * @return: 0 for success, negative value for failure.
> > > > > + *
> > > > > + * This function initialize the AI engine device structure device version
> > > > > + * specific elements such as register addressing related array shift,
> > > > > + * column shift, and row shift; AIE specific device operations, device
> > > > > + * columns resource.
> > > > > + */
> > > > > +int aie_device_init(struct aie_device *adev)
> > > > > +{
> > > > > +       int ret;
> > > > > +
> > > > > +       adev->array_shift = AIE_ARRAY_SHIFT;
> > > > > +       adev->col_shift = AIE_COL_SHIFT;
> > > > > +       adev->row_shift = AIE_ROW_SHIFT;
> > > > > +       adev->ops = &aie_ops;
> > > > > +       adev->num_kernel_regs = ARRAY_SIZE(aie_kernel_regs);
> > > > > +       adev->kernel_regs = aie_kernel_regs;
> > > > > +
> > > > > +       /* Get the columns resource */
> > > > > +       /* Get number of columns from AI engine memory resource */
> > > > > +       ret = aie_resource_initialize(&adev->cols_res, 50);
> > > > > +       if (ret)
> > > > > +               dev_err(&adev->dev, "failed to initialize columns
> > > > > resource.\n");
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
> > > > > b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
> > > > > new file mode 100644
> > > > > index 0000000..2ab2dc8
> > > > > --- /dev/null
> > > > > +++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
> > > > > @@ -0,0 +1,448 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > +/*
> > > > > + * Xilinx AI Engine device driver
> > > > > + *
> > > > > + * Copyright (C) 2020 Xilinx, Inc.
> > > > > + */
> > > > > +
> > > > > +#include <linux/anon_inodes.h>
> > > > > +#include <linux/cdev.h>
> > > > > +#include <linux/delay.h>
> > > > > +#include <linux/device.h>
> > > > > +#include <linux/dma-mapping.h>
> > > > > +#include <linux/file.h>
> > > > > +#include <linux/fs.h>
> > > > > +#include <linux/idr.h>
> > > > > +#include <linux/list.h>
> > > > > +#include <linux/module.h>
> > > > > +#include <linux/mutex.h>
> > > > > +#include <linux/of.h>
> > > > > +#include <linux/of_device.h>
> > > > > +#include <linux/platform_device.h>
> > > > > +#include <linux/slab.h>
> > > > > +#include <linux/uaccess.h>
> > > > > +#include <uapi/linux/xlnx-ai-engine.h>
> > > > > +
> > > > > +#include "ai-engine-internal.h"
> > > > > +
> > > > > +#define AIE_DEV_MAX    (MINORMASK + 1)
> > > > > +
> > > > > +static dev_t aie_major;
> > > > > +struct class *aie_class;
> > > > > +
> > > > > +static DEFINE_IDA(aie_device_ida);
> > > > > +static DEFINE_IDA(aie_minor_ida);
> > > > > +
> > > > > +/**
> > > > > + * aie_get_partition_fd() - Get AI engine partition file descriptor
> > > > > + * @apart: AI engine partition
> > > > > + * @return: file descriptor for AI engine partition for success, or negative
> > > > > + *         value for failure.
> > > > > + *
> > > > > + * This function gets a file descriptor for the AI engine partition.
> > > > > + */
> > > > > +static int aie_get_partition_fd(struct aie_partition *apart)
> > > > > +{
> > > > > +       struct file *filep;
> > > > > +       int ret;
> > > > > +
> > > > > +       /*
> > > > > +        * We can't use anon_inode_getfd() because we need to modify
> > > > > +        * the f_mode flags directly to allow more than just ioctls
> > > > > +        */
> > > > > +       ret = get_unused_fd_flags(O_CLOEXEC);
> > > > > +       if (ret < 0)
> > > > > +               return ret;
> > > > > +
> > > > > +       filep = anon_inode_getfile(dev_name(&apart->dev), &aie_part_fops,
> > > > > +                                  apart, O_RDWR);
> > > > > +       if (IS_ERR(filep)) {
> > > > > +               put_unused_fd(ret);
> > > > > +               ret = PTR_ERR(filep);
> > > > > +               return ret;
> > > > > +       }
> > > > > +       filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
> > > > > +       fd_install(ret, filep);
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_enquire_partitions() - get AI engine partitions information
> > > > > + * @adev: AI engine device
> > > > > + * @query: data struct to store the partition information
> > > > > + * @return: 0 for success, and negative value for failure.
> > > > > + */
> > > > > +static int aie_enquire_partitions(struct aie_device *adev,
> > > > > +                                 struct aie_partition_query *query)
> > > > > +{
> > > > > +       struct aie_partition *apart;
> > > > > +       u32 partition_cnt, i = 0;
> > > > > +       int ret;
> > > > > +
> > > > > +       if (!query->partitions) {
> > > > > +               /*
> > > > > +                * If partitions information buffer is NULL.
> > > > > +                * It is to get the number of partitions.
> > > > > +                */
> > > > > +               query->partition_cnt = 0;
> > > > > +               list_for_each_entry(apart, &adev->partitions, node)
> > > > > +                       query->partition_cnt++;
> > > > > +               return 0;
> > > > > +       }
> > > > > +
> > > > > +       partition_cnt = query->partition_cnt;
> > > > > +       if (!partition_cnt)
> > > > > +               return 0;
> > > > > +
> > > > > +       ret = mutex_lock_interruptible(&adev->mlock);
> > > > > +       if (ret)
> > > > > +               return ret;
> > > > > +
> > > > > +       list_for_each_entry(apart, &adev->partitions, node) {
> > > > > +               struct aie_range_args part;
> > > > > +
> > > > > +               if (i >= partition_cnt)
> > > > > +                       break;
> > > > > +               part.partition_id = apart->partition_id;
> > > > > +               /*
> > > > > +                * TBD: check with PLM that if the partition is programmed
> > > > > +                * and get the UID of the image which is loaded on the AI
> > > > > +                * engine partition.
> > > > > +                */
> > > > > +               part.uid = 0;
> > > > > +               part.range.start.col = apart->range.start.col;
> > > > > +               part.range.start.row = apart->range.start.row;
> > > > > +               part.range.size.col = apart->range.size.col;
> > > > > +               part.range.size.row = apart->range.size.row;
> > > > > +               /* Check if partition is in use */
> > > > > +               part.status = apart->status;
> > > > > +               if (copy_to_user((void __user *)&query->partitions[i], &part,
> > > > > +                                sizeof(part))) {
> > > > > +                       mutex_unlock(&adev->mlock);
> > > > > +                       return -EFAULT;
> > > > > +               }
> > > > > +               i++;
> > > > > +       }
> > > > > +       mutex_unlock(&adev->mlock);
> > > > > +       query->partition_cnt = i;
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_get_partition_from_id() - get AI engine partition from id
> > > > > + * @adev: AI engine device
> > > > > + * @partition_id: partition id to check
> > > > > + * @return: partition pointer if partition exists, otherwise, NULL.
> > > > > + *
> > > > > + * This function checks defined partitions with partition id.
> > > > > + * This function expect the caller to lock mlock of @adev.
> > > > > + */
> > > > > +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
> > > > > +                                               u32 partition_id)
> > > > > +{
> > > > > +       struct aie_partition *apart;
> > > > > +
> > > > > +       list_for_each_entry(apart, &adev->partitions, node) {
> > > > > +               if (apart->partition_id == partition_id)
> > > > > +                       return apart;
> > > > > +       }
> > > > > +
> > > > > +       return NULL;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_request_partition() - request AI engine partition
> > > > > + * @adev: AI engine device
> > > > > + * @req: partition request, includes the requested AI engine information
> > > > > + *      such as partition node ID and the UID of the image which is
> > > > > + *      loaded on the partition.
> > > > > + * @return: partition pointer if partition exists, otherwise, NULL.
> > > > > + *
> > > > > + * This function finds a defined partition which matches the specified
> > > > > + * partition id, request it if it hasn't been requested, and returns it.
> > > > > + */
> > > > > +struct aie_partition *aie_request_partition(struct aie_device *adev,
> > > > > +                                           struct aie_partition_req *req)
> > > > > +{
> > > > > +       struct aie_partition *apart;
> > > > > +       int ret;
> > > > > +
> > > > > +       ret = mutex_lock_interruptible(&adev->mlock);
> > > > > +       if (ret)
> > > > > +               return ERR_PTR(ret);
> > > > > +
> > > > > +       apart = aie_get_partition_from_id(adev, req->partition_id);
> > > > > +       if (!apart) {
> > > > > +               dev_err(&adev->dev,
> > > > > +                       "request partition %u failed, not exist.\n",
> > > > > +                       req->partition_id);
> > > > > +               mutex_unlock(&adev->mlock);
> > > > > +               return ERR_PTR(-EINVAL);
> > > > > +       }
> > > > > +       /*
> > > > > +        * TBD: It will check image UID too to see if the user matches
> > > > > +        * what's loaded in the AI engine partition. And check the meta
> > > > > +        * data to see which resources used by application.
> > > > > +        */
> > > > > +
> > > > > +       ret = mutex_lock_interruptible(&apart->mlock);
> > > > > +       if (ret)
> > > > > +               return ERR_PTR(ret);
> > > > > +
> > > > > +       if (apart->status & XAIE_PART_STATUS_INUSE) {
> > > > > +               mutex_unlock(&apart->mlock);
> > > > > +               dev_err(&adev->dev,
> > > > > +                       "request partition %u failed, partition in use.\n",
> > > > > +                       req->partition_id);
> > > > > +               apart = ERR_PTR(-EBUSY);
> > > > > +       } else {
> > > > > +               /*
> > > > > +                * TBD:
> > > > > +                * 1. setup NOC AXI MM config to only generate error events
> > > > > +                *    for slave error and decode error.
> > > > > +                * 2. scan to see which tiles have been clock gated.
> > > > > +                *
> > > > > +                * This needs to be done before the AI engine partition is
> > > > > +                * exported for user to access.
> > > > > +                */
> > > > > +               apart->status = XAIE_PART_STATUS_INUSE;
> > > > > +               mutex_unlock(&apart->mlock);
> > > > > +       }
> > > > > +       mutex_unlock(&adev->mlock);
> > > > > +
> > > > > +       return apart;
> > > > > +}
> > > > > +
> > > > > +static long xilinx_ai_engine_ioctl(struct file *filp, unsigned int cmd,
> > > > > +                                  unsigned long arg)
> > > > > +{
> > > > > +       struct inode *inode = file_inode(filp);
> > > > > +       struct aie_device *adev = cdev_to_aiedev(inode->i_cdev);
> > > > > +       void __user *argp = (void __user *)arg;
> > > > > +       int ret;
> > > > > +
> > > > > +       switch (cmd) {
> > > > > +       case AIE_ENQUIRE_PART_IOCTL:
> > > > > +       {
> > > > > +               struct aie_partition_query query;
> > > > > +               struct aie_partition_query  __user *uquery_ptr = argp;
> > > > > +
> > > > > +               if (copy_from_user(&query, uquery_ptr, sizeof(query)))
> > > > > +                       return -EFAULT;
> > > > > +               ret = aie_enquire_partitions(adev, &query);
> > > > > +               if (ret < 0)
> > > > > +                       return ret;
> > > > > +               if (copy_to_user((void __user *)&uquery_ptr->partition_cnt,
> > > > > +                                &query.partition_cnt,
> > > > > +                                sizeof(query.partition_cnt)))
> > > > > +                       return -EFAULT;
> > > > > +               break;
> > > > > +       }
> > > > > +       case AIE_REQUEST_PART_IOCTL:
> > > > > +       {
> > > > > +               struct aie_partition_req req;
> > > > > +               struct aie_partition *apart;
> > > > > +
> > > > > +               if (copy_from_user(&req, argp, sizeof(req)))
> > > > > +                       return -EFAULT;
> > > > > +               apart = aie_request_partition(adev, &req);
> > > > > +               if (IS_ERR(apart))
> > > > > +                       return PTR_ERR(apart);
> > > > > +               ret = aie_get_partition_fd(apart);
> > > > > +               if (ret < 0) {
> > > > > +                       dev_err(&apart->dev, "failed to get fd.\n");
> > > > > +                       break;
> > > > > +               }
> > > > > +               break;
> > > > > +       }
> > > > > +       default:
> > > > > +               dev_err(&adev->dev, "Invalid ioctl command %u.\n", cmd);
> > > > > +               ret = -EINVAL;
> > > > > +               break;
> > > > > +       }
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +static const struct file_operations aie_device_fops = {
> > > > > +       .owner          = THIS_MODULE,
> > > > > +       .unlocked_ioctl = xilinx_ai_engine_ioctl,
> > > > > +};
> > > > > +
> > > > > +static void xilinx_ai_engine_release_device(struct device *dev)
> > > > > +{
> > > > > +       struct aie_device *adev = dev_to_aiedev(dev);
> > > > > +
> > > > > +       ida_simple_remove(&aie_device_ida, dev->id);
> > > > > +       ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
> > > > > +       cdev_del(&adev->cdev);
> > > > > +       aie_resource_uninitialize(&adev->cols_res);
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * of_xilinx_ai_engine_part_probe() - probes for AI engine partition nodes
> > > > > + * @adev: AI engine device
> > > > > + *
> > > > > + * This function will probe for children AI engine partition nodes and create
> > > > > + * an AI engine partition instance for each node.
> > > > > + */
> > > > > +static void of_xilinx_ai_engine_part_probe(struct aie_device *adev)
> > > > > +{
> > > > > +       struct device_node *nc;
> > > > > +
> > > > > +       for_each_available_child_of_node(adev->dev.of_node, nc) {
> > > > > +               struct aie_partition *apart;
> > > > > +
> > > > > +               if (of_node_test_and_set_flag(nc, OF_POPULATED))
> > > > > +                       continue;
> > > > > +               apart = of_aie_part_probe(adev, nc);
> > > > > +               if (IS_ERR(apart)) {
> > > > > +                       dev_err(&adev->dev,
> > > > > +                               "Failed to probe AI engine part for %pOF\n",
> > > > > +                               nc);
> > > > > +                       of_node_clear_flag(nc, OF_POPULATED);
> > > > > +               }
> > > > > +       }
> > > > > +}
> > > > > +
> > > > > +static int xilinx_ai_engine_probe(struct platform_device *pdev)
> > > > > +{
> > > > > +       struct aie_device *adev;
> > > > > +       struct device *dev;
> > > > > +       int ret;
> > > > > +
> > > > > +       adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
> > > > > +       if (!adev)
> > > > > +               return -ENOMEM;
> > > > > +       platform_set_drvdata(pdev, adev);
> > > > > +       INIT_LIST_HEAD(&adev->partitions);
> > > > > +       mutex_init(&adev->mlock);
> > > > > +
> > > > > +       adev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > > > > +       if (!adev->res) {
> > > > > +               dev_err(&pdev->dev, "No memory resource.\n");
> > > > > +               return -EINVAL;
> > > > > +       }
> > > > > +       adev->base = devm_ioremap_resource(&pdev->dev, adev->res);
> > > > > +       if (IS_ERR(adev->base)) {
> > > > > +               dev_err(&pdev->dev, "no io memory resource.\n");
> > > > > +               return PTR_ERR(adev->base);
> > > > > +       }
> > > > > +
> > > > > +       /* Initialize AIE device specific instance. */
> > > > > +       ret = aie_device_init(adev);
> > > > > +       if (ret < 0) {
> > > > > +               dev_err(&pdev->dev, "failed to initialize device
> > > > > instance.\n");
> > > > > +               return ret;
> > > > > +       }
> > > > > +
> > > > > +       dev = &adev->dev;
> > > > > +       device_initialize(dev);
> > > > > +       dev->class = aie_class;
> > > > > +       dev->parent = &pdev->dev;
> > > > > +       dev->of_node = pdev->dev.of_node;
> > > > > +
> > > > > +       ret = ida_simple_get(&aie_minor_ida, 0, AIE_DEV_MAX, GFP_KERNEL);
> > > > > +       if (ret < 0)
> > > > > +               goto free_dev;
> > > > > +       dev->devt = MKDEV(MAJOR(aie_major), ret);
> > > > > +       ret = ida_simple_get(&aie_device_ida, 0, 0, GFP_KERNEL);
> > > > > +       if (ret < 0)
> > > > > +               goto free_minor_ida;
> > > > > +       dev->id = ret;
> > > > > +       dev_set_name(&adev->dev, "aie%d", dev->id);
> > > > > +
> > > > > +       cdev_init(&adev->cdev, &aie_device_fops);
> > > > > +       adev->cdev.owner = THIS_MODULE;
> > > > > +       ret = cdev_add(&adev->cdev, dev->devt, 1);
> > > > > +       if (ret)
> > > > > +               goto free_ida;
> > > > > +       /* We can now rely on the release function for cleanup */
> > > > > +       dev->release = xilinx_ai_engine_release_device;
> > > > > +
> > > > > +       ret = device_add(dev);
> > > > > +       if (ret) {
> > > > > +               dev_err(&pdev->dev, "device_add failed: %d\n", ret);
> > > > > +               put_device(dev);
> > > > > +               return ret;
> > > > > +       }
> > > > > +
> > > > > +       of_xilinx_ai_engine_part_probe(adev);
> > > > > +       dev_info(&pdev->dev, "Xilinx AI Engine device(cols=%u) probed\n",
> > > > > +                adev->cols_res.total);
> > > > > +       return 0;
> > > > > +
> > > > > +free_ida:
> > > > > +       ida_simple_remove(&aie_device_ida, dev->id);
> > > > > +free_minor_ida:
> > > > > +       ida_simple_remove(&aie_minor_ida, MINOR(dev->devt));
> > > > > +free_dev:
> > > > > +       put_device(dev);
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +static int xilinx_ai_engine_remove(struct platform_device *pdev)
> > > > > +{
> > > > > +       struct aie_device *adev = platform_get_drvdata(pdev);
> > > > > +       struct aie_partition *apart;
> > > > > +
> > > > > +       list_for_each_entry(apart, &adev->partitions, node)
> > > > > +               aie_part_remove(apart);
> > > > > +
> > > > > +       device_del(&adev->dev);
> > > > > +       put_device(&adev->dev);
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static const struct of_device_id xilinx_ai_engine_of_match[] = {
> > > > > +       { .compatible = "xlnx,ai-engine-v1.0", },
> > > > > +       { /* end of table */ },
> > > > > +};
> > > > > +MODULE_DEVICE_TABLE(of, xilinx_ai_engine_of_match);
> > > > > +
> > > > > +static struct platform_driver xilinx_ai_engine_driver = {
> > > > > +       .probe                  = xilinx_ai_engine_probe,
> > > > > +       .remove                 = xilinx_ai_engine_remove,
> > > > > +       .driver                 = {
> > > > > +               .name           = "xilinx-ai-engine",
> > > > > +               .of_match_table = xilinx_ai_engine_of_match,
> > > > > +       },
> > > > > +};
> > > > > +
> > > > > +static int __init xilinx_ai_engine_init(void)
> > > > > +{
> > > > > +       int ret;
> > > > > +
> > > > > +       ret = alloc_chrdev_region(&aie_major, 0, AIE_DEV_MAX, "aie");
> > > > > +       if (ret < 0) {
> > > > > +               pr_err("aie: failed to allocate aie region\n");
> > > > > +               return ret;
> > > > > +       }
> > > > > +
> > > > > +       aie_class = class_create(THIS_MODULE, "aie");
> > > > > +       if (IS_ERR(aie_class)) {
> > > > > +               pr_err("failed to create aie class\n");
> > > > > +               unregister_chrdev_region(aie_major, AIE_DEV_MAX);
> > > > > +               return PTR_ERR(aie_class);
> > > > > +       }
> > > > > +
> > > > > +       platform_driver_register(&xilinx_ai_engine_driver);
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +postcore_initcall(xilinx_ai_engine_init);
> > > > > +
> > > > > +static void __exit xilinx_ai_engine_exit(void)
> > > > > +{
> > > > > +       platform_driver_unregister(&xilinx_ai_engine_driver);
> > > > > +       class_destroy(aie_class);
> > > > > +       unregister_chrdev_region(aie_major, AIE_DEV_MAX);
> > > > > +}
> > > > > +module_exit(xilinx_ai_engine_exit);
> > > > > +
> > > > > +MODULE_AUTHOR("Xilinx, Inc.");
> > > > > +MODULE_LICENSE("GPL v2");
> > > > > diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
> > > > > b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
> > > > > new file mode 100644
> > > > > index 0000000..6a69946
> > > > > --- /dev/null
> > > > > +++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h
> > > > > @@ -0,0 +1,226 @@
> > > > > +/* SPDX-License-Identifier: GPL-2.0-only */
> > > > > +/*
> > > > > + * Xilinx AI Engine driver internal header
> > > > > + *
> > > > > + * Copyright (C) 2020 Xilinx, Inc.
> > > > > + */
> > > > > +
> > > > > +#ifndef AIE_INTERNAL_H
> > > > > +#define AIE_INTERNAL_H
> > > > > +
> > > > > +#include <linux/bitfield.h>
> > > > > +#include <linux/bits.h>
> > > > > +#include <linux/cdev.h>
> > > > > +#include <linux/device.h>
> > > > > +#include <linux/io.h>
> > > > > +#include <linux/list.h>
> > > > > +#include <linux/mutex.h>
> > > > > +#include <linux/of.h>
> > > > > +#include <linux/of_device.h>
> > > > > +#include <uapi/linux/xlnx-ai-engine.h>
> > > > > +
> > > > > +/*
> > > > > + * Macros for AI engine tile type bitmasks
> > > > > + */
> > > > > +#define AIE_TILE_TYPE_TILE     BIT(0)
> > > > > +#define AIE_TILE_TYPE_SHIMPL   BIT(1)
> > > > > +/* SHIM NOC tile includes SHIM PL and SHIM NOC modules */
> > > > > +#define AIE_TILE_TYPE_SHIMNOC  BIT(2)
> > > > > +
> > > > > +/*
> > > > > + * Macros for attribute property of AI engine registers accessed by kernel
> > > > > + * 0 - 7 bits: tile type bits
> > > > > + * 8 - 15 bits: permission bits. If it is 1, it allows write from userspace
> > > > > + */
> > > > > +#define AIE_REGS_ATTR_TILE_TYPE_SHIFT  0U
> > > > > +#define AIE_REGS_ATTR_PERM_SHIFT       8U
> > > > > +#define AIE_REGS_ATTR_TILE_TYPE_MASK   GENMASK(AIE_REGS_ATTR_PERM_SHIFT - 1,
> > > > > \
> > > > > +                                               AIE_REGS_ATTR_TILE_TYPE_SHIFT)
> > > > > +#define AIE_REGS_ATTR_PERM_MASK                GENMASK(15, \
> > > > > +                            ��                  AIE_REGS_ATTR_PERM_SHIFT)
> > > > > +
> > > > > +/**
> > > > > + * struct aie_tile_regs - contiguous range of AI engine register
> > > > > + *                       within an AI engine tile
> > > > > + * @soff: start offset of the range
> > > > > + * @eoff: end offset of the range
> > > > > + * @attribute: registers attribute. It uses AIE_REGS_ATTR_* macros defined
> > > > > + *            above.
> > > > > + */
> > > > > +struct aie_tile_regs {
> > > > > +       size_t soff;
> > > > > +       size_t eoff;
> > > > > +       u32 attribute;
> > > > > +};
> > > > > +
> > > > > +struct aie_device;
> > > > > +struct aie_partition;
> > > > > +
> > > > > +/**
> > > > > + * struct aie_tile_operations - AI engine device operations
> > > > > + * @get_tile_type: get type of tile based on tile operation
> > > > > + *
> > > > > + * Different AI engine device version has its own device
> > > > > + * operation.
> > > > > + */
> > > > > +struct aie_tile_operations {
> > > > > +       u32 (*get_tile_type)(struct aie_location *loc);
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * struct aie_resource - AI engine resource structure
> > > > > + * @bitmap: resource bitmap
> > > > > + * @total: total number of resource
> > > > > + */
> > > > > +struct aie_resource {
> > > > > +       unsigned long *bitmap;
> > > > > +       u32 total;
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * struct aie_device - AI engine device structure
> > > > > + * @partitions: list of partitions requested
> > > > > + * @cdev: cdev for the AI engine
> > > > > + * @dev: device for the AI engine device
> > > > > + * @mlock: protection for AI engine device operations
> > > > > + * @base: AI engine device base virtual address
> > > > > + * @res: memory resource of AI engine device
> > > > > + * @kernel_regs: array of kernel only registers
> > > > > + * @ops: tile operations
> > > > > + * @size: size of the AI engine address space
> > > > > + * @array_shift: array address shift
> > > > > + * @col_shift: column address shift
> > > > > + * @row_shift: row address shift
> > > > > + * @cols_res: AI engine columns resources to indicate
> > > > > + *           while columns are occupied by partitions.
> > > > > + * @num_kernel_regs: number of kernel only registers range
> > > > > + * @version: AI engine device version
> > > > > + */
> > > > > +struct aie_device {
> > > > > +       struct list_head partitions;
> > > > > +       struct cdev cdev;
> > > > > +       struct device dev;
> > > > > +       struct mutex mlock; /* protection for AI engine partitions */
> > > > > +       void __iomem *base;
> > > > > +       struct resource *res;
> > > > > +       const struct aie_tile_regs *kernel_regs;
> > > > > +       const struct aie_tile_operations *ops;
> > > > > +       size_t size;
> > > > > +       struct aie_resource cols_res;
> > > > > +       u32 array_shift;
> > > > > +       u32 col_shift;
> > > > > +       u32 row_shift;
> > > > > +       u32 num_kernel_regs;
> > > > > +       int version;
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * struct aie_partition - AI engine partition structure
> > > > > + * @node: list node
> > > > > + * @adev: pointer to AI device instance
> > > > > + * @range: range of partition
> > > > > + * @mlock: protection for AI engine partition operations
> > > > > + * @dev: device for the AI engine partition
> > > > > + * @partition_id: partition id. Partition ID is the identifier
> > > > > + *               of the AI engine partition in the system.
> > > > > + * @status: indicate if the partition is in use
> > > > > + */
> > > > > +struct aie_partition {
> > > > > +       struct list_head node;
> > > > > +       struct aie_device *adev;
> > > > > +       struct aie_range range;
> > > > > +       struct mutex mlock; /* protection for AI engine partition operations
> > > > > */
> > > > > +       struct device dev;
> > > > > +       u32 partition_id;
> > > > > +       u32 status;
> > > > > +};
> > > > > +
> > > > > +extern struct class *aie_class;
> > > > > +extern const struct file_operations aie_part_fops;
> > > > > +
> > > > > +#define cdev_to_aiedev(i_cdev) container_of((i_cdev), struct aie_device,
> > > > > cdev)
> > > > > +#define dev_to_aiedev(_dev) container_of((_dev), struct aie_device, dev)
> > > > > +#define dev_to_aiepart(_dev) container_of((_dev), struct aie_partition, dev)
> > > > > +
> > > > > +#define aie_col_mask(adev) ({ \
> > > > > +       struct aie_device *_adev = (adev); \
> > > > > +       GENMASK_ULL(_adev->array_shift - 1, _adev->col_shift);  \
> > > > > +       })
> > > > > +
> > > > > +#define aie_row_mask(adev) ({ \
> > > > > +       struct aie_device *_adev = (adev); \
> > > > > +       GENMASK_ULL(_adev->col_shift - 1, _adev->row_shift);  \
> > > > > +       })
> > > > > +
> > > > > +#define aie_tile_reg_mask(adev) ({ \
> > > > > +       struct aie_device *_adev = (adev); \
> > > > > +       GENMASK_ULL(_adev->row_shift - 1, 0);  \
> > > > > +       })
> > > > > +
> > > > > +/*
> > > > > + * Need to define field get, as AI engine shift mask is not constant.
> > > > > + * Cannot use FIELD_GET()
> > > > > + */
> > > > > +#define aie_tile_reg_field_get(mask, shift, regoff) ( \
> > > > > +       ((regoff) & (mask)) >> (shift))
> > > > > +
> > > > > +#define aie_cal_tile_reg(adev, regoff) ( \
> > > > > +       aie_tile_reg_field_get(aie_tile_reg_mask(adev), 0, regoff))
> > > > > +
> > > > > +/**
> > > > > + * aie_cal_regoff() - calculate register offset to the whole AI engine
> > > > > + *                   device start address
> > > > > + * @adev: AI engine device
> > > > > + * @loc: AI engine tile location
> > > > > + * @regoff_intile: register offset within a tile
> > > > > + * @return: register offset to the whole AI engine device start address
> > > > > + */
> > > > > +static inline u32 aie_cal_regoff(struct aie_device *adev,
> > > > > +                                struct aie_location loc, u32 regoff_intile)
> > > > > +{
> > > > > +       return regoff_intile + (loc.col << adev->col_shift) +
> > > > > +              (loc.row << adev->row_shift);
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_validate_location() - validate tile location within an AI engine
> > > > > + *                          partition
> > > > > + * @apart: AI engine partition
> > > > > + * @loc: AI engine tile location
> > > > > + * @return: return 0 if it is valid, negative value for errors.
> > > > > + *
> > > > > + * This function checks if the AI engine location is within the AI engine
> > > > > + * partition.
> > > > > + */
> > > > > +static inline int aie_validate_location(struct aie_partition *apart,
> > > > > +                                       struct aie_location loc)
> > > > > +{
> > > > > +       if (loc.col < apart->range.start.col ||
> > > > > +           loc.col >= apart->range.start.col + apart->range.size.col ||
> > > > > +           loc.row < apart->range.start.row ||
> > > > > +           loc.row >= apart->range.start.row + apart->range.size.row)
> > > > > +               return -EINVAL;
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +int aie_resource_initialize(struct aie_resource *res, int count);
> > > > > +void aie_resource_uninitialize(struct aie_resource *res);
> > > > > +int aie_resource_check_region(struct aie_resource *res, u32 start,
> > > > > +                             u32 count);
> > > > > +int aie_resource_get_region(struct aie_resource *res, u32 start,
> > > > > +                           u32 count);
> > > > > +void aie_resource_put_region(struct aie_resource *res, int start, u32 count);
> > > > > +
> > > > > +const struct file_operations *aie_part_get_fops(void);
> > > > > +u8 aie_part_in_use(struct aie_partition *apart);
> > > > > +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev,
> > > > > +                                               u32 partition_id);
> > > > > +struct aie_partition *aie_request_partition(struct aie_device *adev,
> > > > > +                                           struct aie_partition_req *req);
> > > > > +struct aie_partition *of_aie_part_probe(struct aie_device *adev,
> > > > > +                                       struct device_node *nc);
> > > > > +void aie_part_remove(struct aie_partition *apart);
> > > > > +
> > > > > +int aie_device_init(struct aie_device *adev);
> > > > > +#endif /* AIE_INTERNAL_H */
> > > > > diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c
> > > > > b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
> > > > > new file mode 100644
> > > > > index 0000000..fc8f9f5
> > > > > --- /dev/null
> > > > > +++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c
> > > > > @@ -0,0 +1,498 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > +/*
> > > > > + * Xilinx AI Engine partition driver
> > > > > + *
> > > > > + * Copyright (C) 2020 Xilinx, Inc.
> > > > > + */
> > > > > +
> > > > > +#include <linux/cdev.h>
> > > > > +#include <linux/delay.h>
> > > > > +#include <linux/device.h>
> > > > > +#include <linux/fs.h>
> > > > > +#include <linux/kernel.h>
> > > > > +#include <linux/list.h>
> > > > > +#include <linux/mm.h>
> > > > > +#include <linux/mman.h>
> > > > > +#include <linux/mmu_context.h>
> > > > > +#include <linux/mutex.h>
> > > > > +#include <linux/of.h>
> > > > > +#include <linux/of_device.h>
> > > > > +#include <linux/slab.h>
> > > > > +#include <linux/uaccess.h>
> > > > > +#include <linux/uio.h>
> > > > > +#include <uapi/linux/xlnx-ai-engine.h>
> > > > > +
> > > > > +#include "ai-engine-internal.h"
> > > > > +
> > > > > +/**
> > > > > + * aie_cal_loc() - calculate tile location from register offset to the AI
> > > > > + *                engine device
> > > > > + * @adev: AI engine device
> > > > > + * @loc: memory pointer to restore returning location information
> > > > > + * @regoff: tile internal register offset
> > > > > + *
> > > > > + * This function returns the tile location.
> > > > > + */
> > > > > +static void aie_cal_loc(struct aie_device *adev,
> > > > > +                       struct aie_location *loc, u64 regoff)
> > > > > +{
> > > > > +       loc->col = (u32)aie_tile_reg_field_get(aie_col_mask(adev),
> > > > > +                                              adev->col_shift, regoff);
> > > > > +       loc->row = (u32)aie_tile_reg_field_get(aie_row_mask(adev),
> > > > > +                                              adev->row_shift, regoff);
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_part_reg_validation() - validate AI engine partition register access
> > > > > + * @apart: AI engine partition
> > > > > + * @offset: AI engine register offset
> > > > > + * @len: len of data to write/read
> > > > > + * @is_write: is the access to write to register
> > > > > + * @return: 0 for success, or negative value for failure.
> > > > > + *
> > > > > + * This function validate if the register to access is within the AI engine
> > > > > + * partition. If it is write access, if the register is writable by user.
> > > > > + */
> > > > > +static int aie_part_reg_validation(struct aie_partition *apart, size_t
> > > > > offset,
> > > > > +                                  size_t len, u8 is_write)
> > > > > +{
> > > > > +       struct aie_device *adev;
> > > > > +       u32 regend32, ttype;
> > > > > +       u64 regoff, regend64;
> > > > > +       struct aie_location loc;
> > > > > +       unsigned int i;
> > > > > +
> > > > > +       adev = apart->adev;
> > > > > +       if (offset % sizeof(u32)) {
> > > > > +               dev_err(&apart->dev,
> > > > > +                       "Invalid reg off(0x%zx), not 32bit aligned.\n",
> > > > > +                       offset);
> > > > > +               return -EINVAL;
> > > > > +       }
> > > > > +
> > > > > +       if (len % sizeof(u32)) {
> > > > > +               dev_err(&apart->dev, "Invalid reg operation len %zu.\n", len);
> > > > > +               return -EINVAL;
> > > > > +       }
> > > > > +
> > > > > +       regoff = aie_cal_tile_reg(adev, offset);
> > > > > +       regend64 = regoff + len;
> > > > > +       if (regend64 >= BIT_ULL(adev->row_shift)) {
> > > > > +               dev_err(&apart->dev,
> > > > > +                       "Invalid reg operation len %zu.\n", len);
> > > > > +               return -EINVAL;
> > > > > +       }
> > > > > +
> > > > > +       aie_cal_loc(adev, &loc, offset);
> > > > > +       if (aie_validate_location(apart, loc)) {
> > > > > +               dev_err(&apart->dev,
> > > > > +                       "Invalid (%d,%d) out of part(%d,%d),(%d,%d)\n",
> > > > > +                       loc.col, loc.row,
> > > > > +                       apart->range.start.col, apart->range.start.row,
> > > > > +                       apart->range.size.col, apart->range.size.row);
> > > > > +               return -EINVAL;
> > > > > +       }
> > > > > +
> > > > > +       if (!is_write)
> > > > > +               return 0;
> > > > > +
> > > > > +       regend32 = lower_32_bits(regend64);
> > > > > +       ttype = adev->ops->get_tile_type(&loc);
> > > > > +       for (i = 0; i < adev->num_kernel_regs; i++) {
> > > > > +               const struct aie_tile_regs *regs;
> > > > > +               u32 rttype, writable;
> > > > > +
> > > > > +               regs = &adev->kernel_regs[i];
> > > > > +               rttype = (regs->attribute & AIE_REGS_ATTR_TILE_TYPE_MASK) >>
> > > > > +                        AIE_REGS_ATTR_TILE_TYPE_SHIFT;
> > > > > +               writable = (regs->attribute & AIE_REGS_ATTR_PERM_MASK) >>
> > > > > +                          AIE_REGS_ATTR_PERM_SHIFT;
> > > > > +               if (!(ttype & rttype))
> > > > > +                       continue;
> > > > > +               if ((regoff >= regs->soff && regoff <= regs->eoff) ||
> > > > > +                   (regend32 >= regs->soff && regend32 <= regs->eoff)) {
> > > > > +                       if (!writable) {
> > > > > +                               dev_err(&apart->dev,
> > > > > +                                       "reg 0x%zx,0x%zx not writable.\n",
> > > > > +                                       offset, len);
> > > > > +                               return -EINVAL;
> > > > > +                       }
> > > > > +               }
> > > > > +       }
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_part_write_register() - AI engine partition write register
> > > > > + * @apart: AI engine partition
> > > > > + * @offset: AI engine register offset
> > > > > + * @len: len of data to write
> > > > > + * @data: data to write
> > > > > + * @mask: mask, if it is non 0, it is mask write.
> > > > > + * @return: number of bytes write for success, or negative value for failure.
> > > > > + *
> > > > > + * This function writes data to the specified registers.
> > > > > + * If the mask is non 0, it is mask write.
> > > > > + */
> > > > > +static int aie_part_write_register(struct aie_partition *apart, size_t
> > > > > offset,
> > > > > +                                  size_t len, void *data, u32 mask)
> > > > > +{
> > > > > +       int ret;
> > > > > +       void __iomem *va;
> > > > > +
> > > > > +       if (mask && len > sizeof(u32)) {
> > > > > +               /* For mask write, only allow 32bit. */
> > > > > +               dev_err(&apart->dev,
> > > > > +                       "failed mask write, len is more that 32bit.\n");
> > > > > +               return -EINVAL;
> > > > > +       }
> > > > > +
> > > > > +       /* offset is expected to be relative to the start of the partition */
> > > > > +       offset += aie_cal_regoff(apart->adev, apart->range.start, 0);
> > > > > +       ret = aie_part_reg_validation(apart, offset, len, 1);
> > > > > +       if (ret < 0) {
> > > > > +               dev_err(&apart->dev, "failed to write to 0x%zx,0x%zx.\n",
> > > > > +                       offset, len);
> > > > > +               return ret;
> > > > > +       }
> > > > > +
> > > > > +       va = apart->adev->base + offset;
> > > > > +       if (!mask) {
> > > > > +               if (len == sizeof(u32))
> > > > > +                       iowrite32(*((u32 *)data),  va);
> > > > > +               else
> > > > > +                       memcpy_toio(va, data, len);
> > > > > +       } else {
> > > > > +               u32 val = ioread32(va);
> > > > > +
> > > > > +               val &= ~mask;
> > > > > +               val |= *((u32 *)data) & mask;
> > > > > +               iowrite32(val, va);
> > > > > +       }
> > > > > +
> > > > > +       return (int)len;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_part_access_regs() - AI engine partition registers access
> > > > > + * @apart: AI engine partition
> > > > > + * @num_reqs: number of access requests
> > > > > + * @reqs: array of registers access
> > > > > + * @return: 0 for success, and negative value for failure.
> > > > > + *
> > > > > + * This function executes AI engine partition register access requests.
> > > > > + */
> > > > > +static int aie_part_access_regs(struct aie_partition *apart, u32 num_reqs,
> > > > > +                               struct aie_reg_args *reqs)
> > > > > +{
> > > > > +       u32 i;
> > > > > +
> > > > > +       for (i = 0; i < num_reqs; i++) {
> > > > > +               struct aie_reg_args *args = &reqs[i];
> > > > > +               int ret;
> > > > > +
> > > > > +               if (args->op != AIE_REG_WRITE) {
> > > > > +                       dev_err(&apart->dev,
> > > > > +                               "Invalid register command type: %u.\n",
> > > > > +                               args->op);
> > > > > +                       return -EINVAL;
> > > > > +               }
> > > > > +               ret = aie_part_write_register(apart,
> > > > > +                                             (size_t)args->offset,
> > > > > +                                             sizeof(args->val),
> > > > > +                                             &args->val, args->mask);
> > > > > +               if (ret < 0) {
> > > > > +                       dev_err(&apart->dev, "reg op %u failed: 0x%llx.\n",
> > > > > +                               args->op, args->offset);
> > > > > +                       return ret;
> > > > > +               }
> > > > > +       }
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static int aie_part_release(struct inode *inode, struct file *filp)
> > > > > +{
> > > > > +       struct aie_partition *apart = filp->private_data;
> > > > > +       int ret;
> > > > > +
> > > > > +       /*
> > > > > +        * TODO: It will need to reset the SHIM columns and gate the
> > > > > +        * tiles of the partition.
> > > > > +        */
> > > > > +       ret = mutex_lock_interruptible(&apart->mlock);
> > > > > +       if (ret)
> > > > > +               return ret;
> > > > > +
> > > > > +       apart->status = 0;
> > > > > +       mutex_unlock(&apart->mlock);
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static const struct vm_operations_struct aie_part_physical_vm_ops = {
> > > > > +#ifdef CONFIG_HAVE_IOREMAP_PROT
> > > > > +       .access = generic_access_phys,
> > > > > +#endif
> > > > > +};
> > > > > +
> > > > > +static int aie_part_mmap(struct file *fp, struct vm_area_struct *vma)
> > > > > +{
> > > > > +       struct aie_partition *apart = fp->private_data;
> > > > > +       struct aie_device *adev = apart->adev;
> > > > > +       unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
> > > > > +       phys_addr_t addr;
> > > > > +       size_t size;
> > > > > +
> > > > > +       if (vma->vm_end < vma->vm_start)
> > > > > +               return -EINVAL;
> > > > > +       /* Only allow userspace directly read registers */
> > > > > +       if (vma->vm_flags & VM_WRITE) {
> > > > > +               dev_err(&apart->dev, "%s: do not support writable mmap.\n",
> > > > > +                       __func__);
> > > > > +               return -EINVAL;
> > > > > +       }
> > > > > +       vma->vm_private_data = apart;
> > > > > +       vma->vm_ops = &aie_part_physical_vm_ops;
> > > > > +       size = apart->range.size.col << adev->col_shift;
> > > > > +       if ((vma->vm_end - vma->vm_start) > (size - offset)) {
> > > > > +               dev_err(&apart->dev,
> > > > > +                       "%s: size exceed.\n", __func__);
> > > > > +               return -EINVAL;
> > > > > +       }
> > > > > +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> > > > > +       /* Calculate the partition address */
> > > > > +       addr = adev->res->start;
> > > > > +       addr += apart->range.start.col << adev->col_shift;
> > > > > +       addr += apart->range.start.row << adev->row_shift;
> > > > > +       addr += offset;
> > > > > +       return remap_pfn_range(vma,
> > > > > +                              vma->vm_start,
> > > > > +                              addr >> PAGE_SHIFT,
> > > > > +                              vma->vm_end - vma->vm_start,
> > > > > +                              vma->vm_page_prot);
> > > > > +}
> > > > > +
> > > > > +static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long
> > > > > arg)
> > > > > +{
> > > > > +       struct aie_partition *apart = fp->private_data;
> > > > > +       void __user *argp = (void __user *)arg;
> > > > > +       long ret;
> > > > > +
> > > > > +       switch (cmd) {
> > > > > +       case AIE_REG_IOCTL:
> > > > > +       {
> > > > > +               struct aie_reg_args raccess;
> > > > > +
> > > > > +               if (copy_from_user(&raccess, argp, sizeof(raccess)))
> > > > > +                       return -EFAULT;
> > > > > +
> > > > > +               ret = mutex_lock_interruptible(&apart->mlock);
> > > > > +               if (ret)
> > > > > +                       return ret;
> > > > > +
> > > > > +               ret = aie_part_access_regs(apart, 1, &raccess);
> > > > > +               mutex_unlock(&apart->mlock);
> > > > > +               break;
> > > > > +       }
> > > > > +       default:
> > > > > +               dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd);
> > > > > +               ret = -EINVAL;
> > > > > +               break;
> > > > > +       }
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +const struct file_operations aie_part_fops = {
> > > > > +       .owner          = THIS_MODULE,
> > > > > +       .release        = aie_part_release,
> > > > > +       .mmap           = aie_part_mmap,
> > > > > +       .unlocked_ioctl = aie_part_ioctl,
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * aie_part_release_device() - release an AI engine partition instance
> > > > > + * @dev: AI engine partition device
> > > > > + *
> > > > > + * It will be called by device driver core when no one holds a valid
> > > > > + * pointer to @dev anymore.
> > > > > + */
> > > > > +static void aie_part_release_device(struct device *dev)
> > > > > +{
> > > > > +       struct aie_partition *apart = dev_to_aiepart(dev);
> > > > > +       struct aie_device *adev = apart->adev;
> > > > > +       int ret;
> > > > > +
> > > > > +       ret = mutex_lock_interruptible(&adev->mlock);
> > > > > +       if (ret) {
> > > > > +               dev_warn(&apart->dev,
> > > > > +                        "getting adev->mlock is interrupted by signal\n");
> > > > > +       }
> > > > > +
> > > > > +       aie_resource_put_region(&adev->cols_res, apart->range.start.col,
> > > > > +                               apart->range.size.col);
> > > > > +       list_del(&apart->node);
> > > > > +       mutex_unlock(&adev->mlock);
> > > > > +       put_device(apart->dev.parent);
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_create_partition() - create AI engine partition instance
> > > > > + * @adev: AI engine device
> > > > > + * @range: AI engine partition range to check. A range describes a group
> > > > > + *        of AI engine tiles.
> > > > > + * @return: created AI engine partition pointer for success, and PTR_ERR
> > > > > + *         for failure.
> > > > > + *
> > > > > + * This function creates an AI engine partition instance.
> > > > > + * It creates AI engine partition, the AI engine partition device and
> > > > > + * the AI engine partition character device.
> > > > > + */
> > > > > +static struct aie_partition *aie_create_partition(struct aie_device *adev,
> > > > > +                                                 struct aie_range *range)
> > > > > +{
> > > > > +       struct aie_partition *apart;
> > > > > +       struct device *dev;
> > > > > +       char devname[32];
> > > > > +       int ret;
> > > > > +
> > > > > +       ret = mutex_lock_interruptible(&adev->mlock);
> > > > > +       if (ret)
> > > > > +               return ERR_PTR(ret);
> > > > > +
> > > > > +       ret = aie_resource_check_region(&adev->cols_res, range->start.col,
> > > > > +                                       range->size.col);
> > > > > +       if (ret != range->start.col) {
> > > > > +               dev_err(&adev->dev, "invalid partition (%u,%u)(%u,%u).\n",
> > > > > +                       range->start.col, range->start.row,
> > > > > +                       range->size.col, range->size.row);
> > > > > +               mutex_unlock(&adev->mlock);
> > > > > +               return ERR_PTR(-EINVAL);
> > > > > +       }
> > > > > +       ret = aie_resource_get_region(&adev->cols_res, range->start.col,
> > > > > +                                     range->size.col);
> > > > > +       if (ret != range->start.col) {
> > > > > +               dev_err(&adev->dev, "failed to get partition
> > > > > (%u,%u)(%u,%u).\n",
> > > > > +                       range->start.col, range->start.row,
> > > > > +                       range->size.col, range->size.row);
> > > > > +               mutex_unlock(&adev->mlock);
> > > > > +               return ERR_PTR(-EFAULT);
> > > > > +       }
> > > > > +       mutex_unlock(&adev->mlock);
> > > > > +
> > > > > +       apart = devm_kzalloc(&adev->dev, sizeof(*apart), GFP_KERNEL);
> > > > > +       if (!apart)
> > > > > +               return ERR_PTR(-ENOMEM);
> > > > > +
> > > > > +       apart->adev = adev;
> > > > > +       memcpy(&apart->range, range, sizeof(*range));
> > > > > +       mutex_init(&apart->mlock);
> > > > > +
> > > > > +       /* Create AI engine partition device */
> > > > > +       dev = &apart->dev;
> > > > > +       device_initialize(dev);
> > > > > +       dev->parent = &adev->dev;
> > > > > +       dev->class = aie_class;
> > > > > +       dev_set_drvdata(dev, apart);
> > > > > +       snprintf(devname, sizeof(devname) - 1, "aiepart_%d_%d",
> > > > > +                apart->range.start.col, apart->range.size.col);
> > > > > +       dev_set_name(dev, devname);
> > > > > +       /* We can now rely on the release function for cleanup */
> > > > > +       dev->release = aie_part_release_device;
> > > > > +       ret = device_add(dev);
> > > > > +       if (ret) {
> > > > > +               dev_err(dev, "device_add failed: %d\n", ret);
> > > > > +               put_device(dev);
> > > > > +               return ERR_PTR(ret);
> > > > > +       }
> > > > > +
> > > > > +       ret = mutex_lock_interruptible(&adev->mlock);
> > > > > +       if (ret) {
> > > > > +               put_device(dev);
> > > > > +               return ERR_PTR(ret);
> > > > > +       }
> > > > > +
> > > > > +       list_add_tail(&apart->node, &adev->partitions);
> > > > > +       mutex_unlock(&adev->mlock);
> > > > > +       get_device(&adev->dev);
> > > > > +       dev_dbg(dev, "created AIE partition device.\n");
> > > > > +
> > > > > +       return apart;
> > > > > +}
> > > > > +
> > > > > +struct aie_partition *
> > > > > +of_aie_part_probe(struct aie_device *adev, struct device_node *nc)
> > > > > +{
> > > > > +       struct aie_partition *apart;
> > > > > +       struct aie_range range;
> > > > > +       u32 partition_id, regs[4];
> > > > > +       int ret;
> > > > > +
> > > > > +       /* Select device driver */
> > > > > +       ret = of_property_read_u32_array(nc, "reg", regs, ARRAY_SIZE(regs));
> > > > > +       if (ret < 0) {
> > > > > +               dev_err(&adev->dev,
> > > > > +                       "probe %pOF failed, no tiles range information.\n",
> > > > > +                       nc);
> > > > > +               return ERR_PTR(ret);
> > > > > +       }
> > > > > +       range.start.col = regs[0];
> > > > > +       range.start.row = regs[1];
> > > > > +       range.size.col = regs[2];
> > > > > +       range.size.row = regs[3];
> > > > > +
> > > > > +       ret = of_property_read_u32_index(nc, "xlnx,partition-id", 0,
> > > > > +                                        &partition_id);
> > > > > +       if (ret < 0) {
> > > > > +               dev_err(&adev->dev,
> > > > > +                       "probe %pOF failed, no partition id.\n", nc);
> > > > > +               return ERR_PTR(ret);
> > > > > +       }
> > > > > +
> > > > > +       ret = mutex_lock_interruptible(&adev->mlock);
> > > > > +       if (ret)
> > > > > +               return ERR_PTR(ret);
> > > > > +
> > > > > +       apart = aie_get_partition_from_id(adev, partition_id);
> > > > > +       mutex_unlock(&adev->mlock);
> > > > > +       if (apart) {
> > > > > +               dev_err(&adev->dev,
> > > > > +                       "probe failed: partition %u exists.\n",
> > > > > +                       partition_id);
> > > > > +               return ERR_PTR(ret);
> > > > > +       }
> > > > > +
> > > > > +       apart = aie_create_partition(adev, &range);
> > > > > +       if (IS_ERR(apart)) {
> > > > > +               dev_err(&adev->dev,
> > > > > +                       "%s: failed to create part(%u,%u),(%u,%u).\n",
> > > > > +                       __func__, range.start.col, range.start.row,
> > > > > +                       range.size.col, range.size.row);
> > > > > +               return apart;
> > > > > +       }
> > > > > +
> > > > > +       of_node_get(nc);
> > > > > +       apart->dev.of_node = nc;
> > > > > +       apart->partition_id = partition_id;
> > > > > +
> > > > > +       dev_info(&adev->dev,
> > > > > +                "AI engine part(%u,%u),(%u,%u), id %u is probed
> > > > > successfully.\n",
> > > > > +                range.start.col, range.start.row,
> > > > > +                range.size.col, range.size.row, apart->partition_id);
> > > > > +
> > > > > +       return apart;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_destroy_part() - destroy AI engine partition
> > > > > + * @apart: AI engine partition
> > > > > + *
> > > > > + * This function will remove AI engine partition.
> > > > > + */
> > > > > +void aie_part_remove(struct aie_partition *apart)
> > > > > +{
> > > > > +       device_del(&apart->dev);
> > > > > +       put_device(&apart->dev);
> > > > > +}
> > > > > diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-res.c
> > > > > b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
> > > > > new file mode 100644
> > > > > index 0000000..36f08bf
> > > > > --- /dev/null
> > > > > +++ b/drivers/misc/xilinx-ai-engine/ai-engine-res.c
> > > > > @@ -0,0 +1,114 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > +/*
> > > > > + * Xilinx AI Engine device driver
> > > > > + *
> > > > > + * Copyright (C) 2020 Xilinx, Inc.
> > > > > + */
> > > > > +
> > > > > +#include <linux/bitmap.h>
> > > > > +
> > > > > +#include "ai-engine-internal.h"
> > > > > +
> > > > > +/**
> > > > > + * aie_resource_initialize() - initialize AI engine resource
> > > > > + * @res: pointer to AI engine resource
> > > > > + * @count: total number of element of this resource
> > > > > + * @return: 0 for success, negative value for failure.
> > > > > + *
> > > > > + * This function will initialize the data structure for the
> > > > > + * resource.
> > > > > + */
> > > > > +int aie_resource_initialize(struct aie_resource *res, int count)
> > > > > +{
> > > > > +       if (!res || !count)
> > > > > +               return -EINVAL;
> > > > > +       res->bitmap = bitmap_zalloc(count, GFP_KERNEL);
> > > > > +       if (!res->bitmap)
> > > > > +               return -ENOMEM;
> > > > > +       res->total = count;
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_resource_uninitialize() - uninitialize AI engine resource
> > > > > + * @res: pointer to AI engine resource
> > > > > + *
> > > > > + * This function will release the AI engine resource data members.
> > > > > + */
> > > > > +void aie_resource_uninitialize(struct aie_resource *res)
> > > > > +{
> > > > > +       res->total = 0;
> > > > > +       if (res->bitmap)
> > > > > +               bitmap_free(res->bitmap);
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_resource_check() - check availability of requested resource
> > > > > + * @res: pointer to AI engine resource to check
> > > > > + * @start: start index of the required resource, it will only be used if
> > > > > + *        @continuous is 1. It will check the available resource starting
> > > > > from
> > > > > + *        @start
> > > > > + * @count: number of requested element
> > > > > + * @return: start resource id if the requested number of resources are
> > > > > available
> > > > > + *         It will return negative value of errors.
> > > > > + *
> > > > > + * This function will check the availability. It will return start resource
> > > > > id
> > > > > + * if the requested number of resources are available.
> > > > > + */
> > > > > +int aie_resource_check_region(struct aie_resource *res,
> > > > > +                             u32 start, u32 count)
> > > > > +{
> > > > > +       unsigned long id;
> > > > > +
> > > > > +       if (!res || !res->bitmap || !count)
> > > > > +               return -EINVAL;
> > > > > +       id = bitmap_find_next_zero_area(res->bitmap, res->total, start,
> > > > > +                                       count, 0);
> > > > > +       if (id >= res->total)
> > > > > +               return -ERANGE;
> > > > > +
> > > > > +       return (int)id;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_resource_get_region() - get requested AI engine resource
> > > > > + * @res: pointer to AI engine resource to check
> > > > > + * @count: number of requested element
> > > > > + * @start: start index of the required resource
> > > > > + * @return: start resource id for success, and negative value for failure.
> > > > > + *
> > > > > + * This function check if the requested AI engine resource is available.
> > > > > + * If it is available, mark it used and return the start resource id.
> > > > > + */
> > > > > +int aie_resource_get_region(struct aie_resource *res, u32 start, u32 count)
> > > > > +{
> > > > > +       unsigned long off;
> > > > > +
> > > > > +       if (!res || !res->bitmap || !count)
> > > > > +               return -EINVAL;
> > > > > +       off = bitmap_find_next_zero_area(res->bitmap, res->total, start,
> > > > > +                                        count, 0);
> > > > > +       if (off >= res->total) {
> > > > > +               pr_err("Failed to get available AI engine resource.\n");
> > > > > +               return -ERANGE;
> > > > > +       }
> > > > > +       bitmap_set(res->bitmap, off, count);
> > > > > +
> > > > > +       return (int)off;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * aie_resource_put_region() - release requested AI engine resource
> > > > > + * @res: pointer to AI engine resource to check
> > > > > + * @start: start index of the resource to release
> > > > > + * @count: number of elements to release
> > > > > + *
> > > > > + * This function release the requested AI engine resource.
> > > > > + */
> > > > > +void aie_resource_put_region(struct aie_resource *res, int start, u32 count)
> > > > > +{
> > > > > +       if (!res || !count)
> > > > > +               return;
> > > > > +       bitmap_clear(res->bitmap, start, count);
> > > > > +}
> > > > > diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-
> > > > > engine.h
> > > > > new file mode 100644
> > > > > index 0000000..acbc781
> > > > > --- /dev/null
> > > > > +++ b/include/uapi/linux/xlnx-ai-engine.h
> > > > > @@ -0,0 +1,107 @@
> > > > > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > > > > +/*
> > > > > + * Copyright (c) 2020, Xilinx Inc.
> > > > > + */
> > > > > +
> > > > > +#ifndef _UAPI_AI_ENGINE_H_
> > > > > +#define _UAPI_AI_ENGINE_H_
> > > > > +
> > > > > +#include <linux/ioctl.h>
> > > > > +#include <linux/types.h>
> > > > > +
> > > > > +enum aie_reg_op {
> > > > > +       AIE_REG_WRITE,
> > > > > +};
> > > > > +
> > > > > +/* AI engine partition is in use */
> > > > > +#define XAIE_PART_STATUS_INUSE         (1U << 0)
> > > > > +
> > > > > +/**
> > > > > + * struct aie_location - AIE location information
> > > > > + * @col: column id
> > > > > + * @row: row id
> > > > > + */
> > > > > +struct aie_location {
> > > > > +       __u32 col;
> > > > > +       __u32 row;
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * struct aie_range - AIE range information
> > > > > + * @start: start tile location
> > > > > + * @size: size of the range, number of columns and rows
> > > > > + */
> > > > > +struct aie_range {
> > > > > +       struct aie_location start;
> > > > > +       struct aie_location size;
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * struct aie_reg_args - AIE access register arguments
> > > > > + * @op: if this request is to read, write or poll register
> > > > > + * @mask: mask for mask write, 0 for not mask write
> > > > > + * @offset: offset of register to the start of an AI engine partition
> > > > > + * @val: value to write or get
> > > > > + */
> > > > > +struct aie_reg_args {
> > > > > +       enum aie_reg_op op;
> > > > > +       __u32 mask;
> > > > > +       __u64 offset;
> > > > > +       __u32 val;
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * struct aie_range_args - AIE range request arguments
> > > > > + * @partition_id: partition id. It is used to identify the
> > > > > + *               AI engine partition in the system.
> > > > > + * @uid: image identifier loaded on the AI engine partition
> > > > > + * @range: range of AIE tiles
> > > > > + * @status: indicate if the AI engine is in use.
> > > > > + *         0 means not in used, otherwise, in use.
> > > > > + */
> > > > > +struct aie_range_args {
> > > > > +       __u32 partition_id;
> > > > > +       __u32 uid;
> > > > > +       struct aie_range range;
> > > > > +       __u32 status;
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * struct aie_partition_query - AIE partition query arguments
> > > > > + * @partition_cnt: number of defined partitions in the system
> > > > > + * @partitions: buffer to store defined partitions information.
> > > > > + */
> > > > > +struct aie_partition_query {
> > > > > +       struct aie_range_args *partitions;
> > > > > +       __u32 partition_cnt;
> > > > > +};
> > > > > +
> > > > > +/**
> > > > > + * struct aie_partition_req - AIE request partition arguments
> > > > > + * @partition_id: partition node id. It is used to identify the AI engine
> > > > > + *               partition in the system.
> > > > > + * @uid: image identifier loaded on the AI engine partition
> > > > > + * @meta_data: meta data to indicate which resources used by application.
> > > > > + * @flag: used for application to indicate particular driver requirements
> > > > > + *       application wants to have for the partition. e.g. do not clean
> > > > > + *       resource when closing the partition.
> > > > > + */
> > > > > +struct aie_partition_req {
> > > > > +       __u32 partition_id;
> > > > > +       __u32 uid;
> > > > > +       __u64 meta_data;
> > > > > +       __u32 flag;
> > > > > +};
> > > > > +
> > > > > +#define AIE_IOCTL_BASE 'A'
> > > > > +
> > > > > +/* AI engine device IOCTL operations */
> > > > > +#define AIE_ENQUIRE_PART_IOCTL         _IOWR(AIE_IOCTL_BASE, 0x1, \
> > > > > +                                             struct aie_partition_query)
> > > > > +#define AIE_REQUEST_PART_IOCTL         _IOR(AIE_IOCTL_BASE, 0x2, \
> > > > > +                                            struct aie_partition_req)
> > > > > +
> > > > > +/* AI engine partition IOCTL operations */
> > > > > +#define AIE_REG_IOCTL                  _IOWR(AIE_IOCTL_BASE, 0x8, \
> > > > > +                                             struct aie_reg_args)
> > > > > +#endif
> > > _______________________________________________
> > > dri-devel mailing list
> > > [email protected]
> > > https://lists.freedesktop.org/mailman/listinfo/dri-devel
> _______________________________________________
> dri-devel mailing list
> [email protected]
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch