diff --git a/MAINTAINERS b/MAINTAINERS
index 2daa6ee..1e36294 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19287,6 +19287,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 <wendy.liang@xilinx.com>
+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 <eli.billauer@gmail.com>
 L:	linux-kernel@vger.kernel.org
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..5a56a92
--- /dev/null
+++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c
@@ -0,0 +1,452 @@
+// 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/kernel.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;
+	u64 user_part;
+	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;
+
+	user_part = query->partitions;
+	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(u64_to_user_ptr(user_part), &part,
+				 sizeof(part))) {
+			mutex_unlock(&adev->mlock);
+			return -EFAULT;
+		}
+		user_part += sizeof(part);
+		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..2a0239b
--- /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>
+
+/* AI engine register access operation */
+#define AIE_REG_WRITE 0
+
+/* 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 {
+	__u32 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.
+ *		It is an array of struct aie_range_args.
+ */
+struct aie_partition_query {
+	__u64 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
