[RFC,Xilinx,Alveo,3/6] Add platform drivers for various IPs and frameworks
diff mbox series

Message ID 20190319215401.6562-4-sonal.santan@xilinx.com
State New, archived
Headers show
Series
  • Xilinx PCIe accelerator driver
Related show

Commit Message

sonal.santan@xilinx.com March 19, 2019, 9:53 p.m. UTC
From: Sonal Santan <sonal.santan@xilinx.com>

Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>
---
 drivers/gpu/drm/xocl/subdev/dna.c          |  356 +++
 drivers/gpu/drm/xocl/subdev/feature_rom.c  |  412 +++
 drivers/gpu/drm/xocl/subdev/firewall.c     |  389 +++
 drivers/gpu/drm/xocl/subdev/fmgr.c         |  198 ++
 drivers/gpu/drm/xocl/subdev/icap.c         | 2859 ++++++++++++++++++
 drivers/gpu/drm/xocl/subdev/mailbox.c      | 1868 ++++++++++++
 drivers/gpu/drm/xocl/subdev/mb_scheduler.c | 3059 ++++++++++++++++++++
 drivers/gpu/drm/xocl/subdev/microblaze.c   |  722 +++++
 drivers/gpu/drm/xocl/subdev/mig.c          |  256 ++
 drivers/gpu/drm/xocl/subdev/sysmon.c       |  385 +++
 drivers/gpu/drm/xocl/subdev/xdma.c         |  510 ++++
 drivers/gpu/drm/xocl/subdev/xmc.c          | 1480 ++++++++++
 drivers/gpu/drm/xocl/subdev/xvc.c          |  461 +++
 13 files changed, 12955 insertions(+)
 create mode 100644 drivers/gpu/drm/xocl/subdev/dna.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/feature_rom.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/firewall.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/fmgr.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/icap.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/mailbox.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/mb_scheduler.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/microblaze.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/mig.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/sysmon.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/xdma.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/xmc.c
 create mode 100644 drivers/gpu/drm/xocl/subdev/xvc.c

Patch
diff mbox series

diff --git a/drivers/gpu/drm/xocl/subdev/dna.c b/drivers/gpu/drm/xocl/subdev/dna.c
new file mode 100644
index 000000000000..991d98e5b9aa
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/dna.c
@@ -0,0 +1,356 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2018 Xilinx, Inc. All rights reserved.
+ *
+ * Authors: Chien-Wei Lan <chienwei@xilinx.com>
+ *
+ */
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/vmalloc.h>
+#include "../xocl_drv.h"
+#include <drm/xmgmt_drm.h>
+
+/* Registers are defined in pg150-ultrascale-memory-ip.pdf:
+ * AXI4-Lite Slave Control/Status Register Map
+ */
+#define XLNX_DNA_MEMORY_MAP_MAGIC_IS_DEFINED                        (0x3E4D7732)
+#define XLNX_DNA_MAJOR_MINOR_VERSION_REGISTER_OFFSET                0x00          //  RO
+#define XLNX_DNA_REVISION_REGISTER_OFFSET                           0x04          //  RO
+#define XLNX_DNA_CAPABILITY_REGISTER_OFFSET                         0x08          //  RO
+//#define XLNX_DNA_SCRATCHPAD_REGISTER_OFFSET                         (0x0C)          //  RO (31-1) + RW (0)
+#define XLNX_DNA_STATUS_REGISTER_OFFSET                             0x10            //  RO
+#define XLNX_DNA_FSM_DNA_WORD_WRITE_COUNT_REGISTER_OFFSET           (0x14)          //  RO
+#define XLNX_DNA_FSM_CERTIFICATE_WORD_WRITE_COUNT_REGISTER_OFFSET   (0x18)          //  RO
+#define XLNX_DNA_MESSAGE_START_AXI_ONLY_REGISTER_OFFSET             (0x20)          //  RO (31-1) + RW (0)
+#define XLNX_DNA_READBACK_REGISTER_2_OFFSET                         0x40            //  RO XLNX_DNA_BOARD_DNA_95_64
+#define XLNX_DNA_READBACK_REGISTER_1_OFFSET                         0x44            //  RO XLNX_DNA_BOARD_DNA_63_32
+#define XLNX_DNA_READBACK_REGISTER_0_OFFSET                         0x48            //  RO XLNX_DNA_BOARD_DNA_31_0
+#define XLNX_DNA_DATA_AXI_ONLY_REGISTER_OFFSET                      (0x80)          //  WO
+#define XLNX_DNA_CERTIFICATE_DATA_AXI_ONLY_REGISTER_OFFSET          (0xC0)          //  WO - 512 bit aligned.
+#define XLNX_DNA_MAX_ADDRESS_WORDS                                  (0xC4)
+
+struct xocl_xlnx_dna {
+	void __iomem		*base;
+	struct device		*xlnx_dna_dev;
+	struct mutex		xlnx_dna_lock;
+};
+
+static ssize_t status_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xlnx_dna *xlnx_dna = dev_get_drvdata(dev);
+	u32 status;
+
+	status = ioread32(xlnx_dna->base+XLNX_DNA_STATUS_REGISTER_OFFSET);
+
+	return sprintf(buf, "0x%x\n", status);
+}
+static DEVICE_ATTR_RO(status);
+
+static ssize_t dna_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xlnx_dna *xlnx_dna = dev_get_drvdata(dev);
+	uint32_t dna96_64, dna63_32, dna31_0;
+
+	dna96_64 = ioread32(xlnx_dna->base+XLNX_DNA_READBACK_REGISTER_2_OFFSET);
+	dna63_32 = ioread32(xlnx_dna->base+XLNX_DNA_READBACK_REGISTER_1_OFFSET);
+	dna31_0  = ioread32(xlnx_dna->base+XLNX_DNA_READBACK_REGISTER_0_OFFSET);
+
+	return sprintf(buf, "%08x%08x%08x\n", dna96_64, dna63_32, dna31_0);
+}
+static DEVICE_ATTR_RO(dna);
+
+static ssize_t capability_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xlnx_dna *xlnx_dna = dev_get_drvdata(dev);
+	u32 capability;
+
+	capability = ioread32(xlnx_dna->base+XLNX_DNA_CAPABILITY_REGISTER_OFFSET);
+
+	return sprintf(buf, "0x%x\n", capability);
+}
+static DEVICE_ATTR_RO(capability);
+
+
+static ssize_t dna_version_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xlnx_dna *xlnx_dna = dev_get_drvdata(dev);
+	u32 version;
+
+	version = ioread32(xlnx_dna->base+XLNX_DNA_MAJOR_MINOR_VERSION_REGISTER_OFFSET);
+
+	return sprintf(buf, "%d.%d\n", version>>16, version & 0xffff);
+}
+static DEVICE_ATTR_RO(dna_version);
+
+static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xlnx_dna *xlnx_dna = dev_get_drvdata(dev);
+	u32 revision;
+
+	revision = ioread32(xlnx_dna->base+XLNX_DNA_REVISION_REGISTER_OFFSET);
+
+	return sprintf(buf, "%d\n", revision);
+}
+static DEVICE_ATTR_RO(revision);
+
+static struct attribute *xlnx_dna_attributes[] = {
+	&dev_attr_status.attr,
+	&dev_attr_dna.attr,
+	&dev_attr_capability.attr,
+	&dev_attr_dna_version.attr,
+	&dev_attr_revision.attr,
+	NULL
+};
+
+static const struct attribute_group xlnx_dna_attrgroup = {
+	.attrs = xlnx_dna_attributes,
+};
+
+static uint32_t dna_status(struct platform_device *pdev)
+{
+	struct xocl_xlnx_dna *xlnx_dna = platform_get_drvdata(pdev);
+	uint32_t status = 0;
+	uint8_t retries = 10;
+	bool rsa4096done = false;
+
+	if (!xlnx_dna)
+		return status;
+
+	while (!rsa4096done && retries) {
+		status = ioread32(xlnx_dna->base+XLNX_DNA_STATUS_REGISTER_OFFSET);
+		if (status>>8 & 0x1) {
+			rsa4096done = true;
+			break;
+		}
+		msleep(1);
+		retries--;
+	}
+
+	if (retries == 0)
+		return -EBUSY;
+
+	status = ioread32(xlnx_dna->base+XLNX_DNA_STATUS_REGISTER_OFFSET);
+
+	return status;
+}
+
+static uint32_t dna_capability(struct platform_device *pdev)
+{
+	struct xocl_xlnx_dna *xlnx_dna = platform_get_drvdata(pdev);
+	u32 capability = 0;
+
+	if (!xlnx_dna)
+		return capability;
+
+	capability = ioread32(xlnx_dna->base+XLNX_DNA_CAPABILITY_REGISTER_OFFSET);
+
+	return capability;
+}
+
+static void dna_write_cert(struct platform_device *pdev, const uint32_t *cert, uint32_t len)
+{
+	struct xocl_xlnx_dna *xlnx_dna = platform_get_drvdata(pdev);
+	int i, j, k;
+	u32 status = 0, words;
+	uint8_t retries = 100;
+	bool sha256done = false;
+	uint32_t convert;
+	uint32_t sign_start, message_words = (len-512)>>2;
+
+	sign_start = message_words;
+
+	if (!xlnx_dna)
+		return;
+
+	iowrite32(0x1, xlnx_dna->base+XLNX_DNA_MESSAGE_START_AXI_ONLY_REGISTER_OFFSET);
+	status = ioread32(xlnx_dna->base+XLNX_DNA_STATUS_REGISTER_OFFSET);
+	xocl_info(&pdev->dev, "Start: status %08x", status);
+
+	for (i = 0; i < message_words; i += 16) {
+
+		retries = 100;
+		sha256done = false;
+
+		while (!sha256done && retries) {
+			status = ioread32(xlnx_dna->base+XLNX_DNA_STATUS_REGISTER_OFFSET);
+			if (!(status >> 4 & 0x1)) {
+				sha256done = true;
+				break;
+			}
+			msleep(10);
+			retries--;
+		}
+		for (j = 0; j < 16; ++j) {
+			convert = (*(cert+i+j) >> 24 & 0xff) | (*(cert+i+j) >> 8 & 0xff00) |
+				(*(cert+i+j) << 8 & 0xff0000) | ((*(cert+i+j) & 0xff) << 24);
+			iowrite32(convert, xlnx_dna->base+XLNX_DNA_DATA_AXI_ONLY_REGISTER_OFFSET+j*4);
+		}
+	}
+	retries = 100;
+	sha256done = false;
+	while (!sha256done && retries) {
+		status = ioread32(xlnx_dna->base+XLNX_DNA_STATUS_REGISTER_OFFSET);
+		if (!(status >> 4 & 0x1)) {
+			sha256done = true;
+			break;
+		}
+		msleep(10);
+		retries--;
+	}
+
+	status = ioread32(xlnx_dna->base+XLNX_DNA_STATUS_REGISTER_OFFSET);
+	words  = ioread32(xlnx_dna->base+XLNX_DNA_FSM_DNA_WORD_WRITE_COUNT_REGISTER_OFFSET);
+	xocl_info(&pdev->dev, "Message: status %08x dna words %d", status, words);
+
+	for (k = 0; k < 128; k += 16) {
+		for (i = 0; i < 16; i++) {
+			j = k+i+sign_start;
+			convert = (*(cert + j) >> 24 & 0xff) | (*(cert + j) >> 8 & 0xff00) |
+				(*(cert + j) << 8 & 0xff0000) | ((*(cert + j) & 0xff) << 24);
+			iowrite32(convert, xlnx_dna->base+XLNX_DNA_CERTIFICATE_DATA_AXI_ONLY_REGISTER_OFFSET + i * 4);
+		}
+	}
+
+	status = ioread32(xlnx_dna->base+XLNX_DNA_STATUS_REGISTER_OFFSET);
+	words  = ioread32(xlnx_dna->base+XLNX_DNA_FSM_CERTIFICATE_WORD_WRITE_COUNT_REGISTER_OFFSET);
+	xocl_info(&pdev->dev, "Signature: status %08x certificate words %d", status, words);
+}
+
+static struct xocl_dna_funcs dna_ops = {
+	.status = dna_status,
+	.capability = dna_capability,
+	.write_cert = dna_write_cert,
+};
+
+
+static void mgmt_sysfs_destroy_xlnx_dna(struct platform_device *pdev)
+{
+	struct xocl_xlnx_dna *xlnx_dna;
+
+	xlnx_dna = platform_get_drvdata(pdev);
+
+	sysfs_remove_group(&pdev->dev.kobj, &xlnx_dna_attrgroup);
+
+}
+
+static int mgmt_sysfs_create_xlnx_dna(struct platform_device *pdev)
+{
+	struct xocl_xlnx_dna *xlnx_dna;
+	struct xocl_dev_core *core;
+	int err;
+
+	xlnx_dna = platform_get_drvdata(pdev);
+	core = XDEV(xocl_get_xdev(pdev));
+
+	err = sysfs_create_group(&pdev->dev.kobj, &xlnx_dna_attrgroup);
+	if (err) {
+		xocl_err(&pdev->dev, "create pw group failed: 0x%x", err);
+		goto create_grp_failed;
+	}
+
+	return 0;
+
+create_grp_failed:
+	return err;
+}
+
+static int xlnx_dna_probe(struct platform_device *pdev)
+{
+	struct xocl_xlnx_dna *xlnx_dna;
+	struct resource *res;
+	int err;
+
+	xlnx_dna = devm_kzalloc(&pdev->dev, sizeof(*xlnx_dna), GFP_KERNEL);
+	if (!xlnx_dna)
+		return -ENOMEM;
+
+	xlnx_dna->base = devm_kzalloc(&pdev->dev, sizeof(void __iomem *), GFP_KERNEL);
+	if (!xlnx_dna->base)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		xocl_err(&pdev->dev, "resource is NULL");
+		return -EINVAL;
+	}
+	xocl_info(&pdev->dev, "IO start: 0x%llx, end: 0x%llx",
+		res->start, res->end);
+
+	xlnx_dna->base = ioremap_nocache(res->start, res->end - res->start + 1);
+	if (!xlnx_dna->base) {
+		err = -EIO;
+		xocl_err(&pdev->dev, "Map iomem failed");
+		goto failed;
+	}
+
+	platform_set_drvdata(pdev, xlnx_dna);
+
+	err = mgmt_sysfs_create_xlnx_dna(pdev);
+	if (err)
+		goto create_xlnx_dna_failed;
+
+	xocl_subdev_register(pdev, XOCL_SUBDEV_DNA, &dna_ops);
+
+	return 0;
+
+create_xlnx_dna_failed:
+	platform_set_drvdata(pdev, NULL);
+failed:
+	return err;
+}
+
+
+static int xlnx_dna_remove(struct platform_device *pdev)
+{
+	struct xocl_xlnx_dna	*xlnx_dna;
+
+	xlnx_dna = platform_get_drvdata(pdev);
+	if (!xlnx_dna) {
+		xocl_err(&pdev->dev, "driver data is NULL");
+		return -EINVAL;
+	}
+
+	mgmt_sysfs_destroy_xlnx_dna(pdev);
+
+	if (xlnx_dna->base)
+		iounmap(xlnx_dna->base);
+
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(&pdev->dev, xlnx_dna);
+
+	return 0;
+}
+
+struct platform_device_id xlnx_dna_id_table[] = {
+	{ XOCL_DNA, 0 },
+	{ },
+};
+
+static struct platform_driver	xlnx_dna_driver = {
+	.probe		= xlnx_dna_probe,
+	.remove		= xlnx_dna_remove,
+	.driver		= {
+		.name = "xocl_dna",
+	},
+	.id_table = xlnx_dna_id_table,
+};
+
+int __init xocl_init_dna(void)
+{
+	return platform_driver_register(&xlnx_dna_driver);
+}
+
+void xocl_fini_dna(void)
+{
+	platform_driver_unregister(&xlnx_dna_driver);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/feature_rom.c b/drivers/gpu/drm/xocl/subdev/feature_rom.c
new file mode 100644
index 000000000000..f898af6844aa
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/feature_rom.c
@@ -0,0 +1,412 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2016-2019 Xilinx, Inc. All rights reserved.
+ *
+ * Authors:
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include "../xclfeatures.h"
+#include "../xocl_drv.h"
+
+#define	MAGIC_NUM	0x786e6c78
+struct feature_rom {
+	void __iomem		*base;
+
+	struct FeatureRomHeader	header;
+	unsigned int            dsa_version;
+	bool			unified;
+	bool			mb_mgmt_enabled;
+	bool			mb_sche_enabled;
+	bool			are_dev;
+	bool			aws_dev;
+};
+
+static ssize_t VBNV_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct feature_rom *rom = platform_get_drvdata(to_platform_device(dev));
+
+	return sprintf(buf, "%s\n", rom->header.VBNVName);
+}
+static DEVICE_ATTR_RO(VBNV);
+
+static ssize_t dr_base_addr_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct feature_rom *rom = platform_get_drvdata(to_platform_device(dev));
+
+	//TODO: Fix: DRBaseAddress no longer required in feature rom
+	if (rom->header.MajorVersion >= 10)
+		return sprintf(buf, "%llu\n", rom->header.DRBaseAddress);
+	else
+		return sprintf(buf, "%u\n", 0);
+}
+static DEVICE_ATTR_RO(dr_base_addr);
+
+static ssize_t ddr_bank_count_max_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct feature_rom *rom = platform_get_drvdata(to_platform_device(dev));
+
+	return sprintf(buf, "%d\n", rom->header.DDRChannelCount);
+}
+static DEVICE_ATTR_RO(ddr_bank_count_max);
+
+static ssize_t ddr_bank_size_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct feature_rom *rom = platform_get_drvdata(to_platform_device(dev));
+
+	return sprintf(buf, "%d\n", rom->header.DDRChannelSize);
+}
+static DEVICE_ATTR_RO(ddr_bank_size);
+
+static ssize_t timestamp_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct feature_rom *rom = platform_get_drvdata(to_platform_device(dev));
+
+	return sprintf(buf, "%llu\n", rom->header.TimeSinceEpoch);
+}
+static DEVICE_ATTR_RO(timestamp);
+
+static ssize_t FPGA_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct feature_rom *rom = platform_get_drvdata(to_platform_device(dev));
+
+	return sprintf(buf, "%s\n", rom->header.FPGAPartName);
+}
+static DEVICE_ATTR_RO(FPGA);
+
+static struct attribute *rom_attrs[] = {
+	&dev_attr_VBNV.attr,
+	&dev_attr_dr_base_addr.attr,
+	&dev_attr_ddr_bank_count_max.attr,
+	&dev_attr_ddr_bank_size.attr,
+	&dev_attr_timestamp.attr,
+	&dev_attr_FPGA.attr,
+	NULL,
+};
+
+static struct attribute_group rom_attr_group = {
+	.attrs = rom_attrs,
+};
+
+static unsigned int dsa_version(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	return rom->dsa_version;
+}
+
+static bool is_unified(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	return rom->unified;
+}
+
+static bool mb_mgmt_on(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	return rom->mb_mgmt_enabled;
+}
+
+static bool mb_sched_on(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	return rom->mb_sche_enabled && !XOCL_DSA_MB_SCHE_OFF(xocl_get_xdev(pdev));
+}
+
+static uint32_t *get_cdma_base_addresses(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	return (rom->header.FeatureBitMap & CDMA) ? rom->header.CDMABaseAddress : 0;
+}
+
+static u16 get_ddr_channel_count(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	return rom->header.DDRChannelCount;
+}
+
+static u64 get_ddr_channel_size(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	return rom->header.DDRChannelSize;
+}
+
+static u64 get_timestamp(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	return rom->header.TimeSinceEpoch;
+}
+
+static bool is_are(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	return rom->are_dev;
+}
+
+static bool is_aws(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	return rom->aws_dev;
+}
+
+static bool verify_timestamp(struct platform_device *pdev, u64 timestamp)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	xocl_info(&pdev->dev, "DSA timestamp: 0x%llx",
+		rom->header.TimeSinceEpoch);
+	xocl_info(&pdev->dev, "Verify timestamp: 0x%llx", timestamp);
+	return (rom->header.TimeSinceEpoch == timestamp);
+}
+
+static void get_raw_header(struct platform_device *pdev, void *header)
+{
+	struct feature_rom *rom;
+
+	rom = platform_get_drvdata(pdev);
+	BUG_ON(!rom);
+
+	memcpy(header, &rom->header, sizeof(rom->header));
+}
+
+static struct xocl_rom_funcs rom_ops = {
+	.dsa_version = dsa_version,
+	.is_unified = is_unified,
+	.mb_mgmt_on = mb_mgmt_on,
+	.mb_sched_on = mb_sched_on,
+	.cdma_addr = get_cdma_base_addresses,
+	.get_ddr_channel_count = get_ddr_channel_count,
+	.get_ddr_channel_size = get_ddr_channel_size,
+	.is_are = is_are,
+	.is_aws = is_aws,
+	.verify_timestamp = verify_timestamp,
+	.get_timestamp = get_timestamp,
+	.get_raw_header = get_raw_header,
+};
+
+static int feature_rom_probe(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+	struct resource *res;
+	u32	val;
+	u16	vendor, did;
+	char	*tmp;
+	int	ret;
+
+	rom = devm_kzalloc(&pdev->dev, sizeof(*rom), GFP_KERNEL);
+	if (!rom)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	rom->base = ioremap_nocache(res->start, res->end - res->start + 1);
+	if (!rom->base) {
+		ret = -EIO;
+		xocl_err(&pdev->dev, "Map iomem failed");
+		goto failed;
+	}
+
+	val = ioread32(rom->base);
+	if (val != MAGIC_NUM) {
+		vendor = XOCL_PL_TO_PCI_DEV(pdev)->vendor;
+		did = XOCL_PL_TO_PCI_DEV(pdev)->device;
+		if (vendor == 0x1d0f && (did == 0x1042 || did == 0xf010)) { // MAGIC, we should define elsewhere
+			xocl_info(&pdev->dev,
+				"Found AWS VU9P Device without featureROM");
+			/*
+			 * This is AWS device. Fill the FeatureROM struct.
+			 * Right now it doesn't have FeatureROM
+			 */
+			memset(rom->header.EntryPointString, 0,
+				sizeof(rom->header.EntryPointString));
+			strncpy(rom->header.EntryPointString, "xlnx", 4);
+			memset(rom->header.FPGAPartName, 0,
+				sizeof(rom->header.FPGAPartName));
+			strncpy(rom->header.FPGAPartName, "AWS VU9P", 8);
+			memset(rom->header.VBNVName, 0,
+				sizeof(rom->header.VBNVName));
+			strncpy(rom->header.VBNVName,
+				"xilinx_aws-vu9p-f1_dynamic_5_0", 35);
+			rom->header.MajorVersion = 4;
+			rom->header.MinorVersion = 0;
+			rom->header.VivadoBuildID = 0xabcd;
+			rom->header.IPBuildID = 0xabcd;
+			rom->header.TimeSinceEpoch = 0xabcd;
+			rom->header.DDRChannelCount = 4;
+			rom->header.DDRChannelSize = 16;
+			rom->header.FeatureBitMap = 0x0;
+			rom->header.FeatureBitMap = UNIFIED_PLATFORM;
+			rom->unified = true;
+			rom->aws_dev = true;
+
+			xocl_info(&pdev->dev, "Enabling AWS dynamic 5.0 DSA");
+		} else {
+			xocl_err(&pdev->dev, "Magic number does not match, actual 0x%x, expected 0x%x",
+					val, MAGIC_NUM);
+			ret = -ENODEV;
+			goto failed;
+		}
+	}
+
+	xocl_memcpy_fromio(&rom->header, rom->base, sizeof(rom->header));
+
+	if (strstr(rom->header.VBNVName, "-xare")) {
+		/*
+		 * ARE device, ARE is mapped like another DDR inside FPGA;
+		 * map_connects as M04_AXI
+		 */
+		rom->header.DDRChannelCount = rom->header.DDRChannelCount - 1;
+		rom->are_dev = true;
+	}
+
+	rom->dsa_version = 0;
+	if (strstr(rom->header.VBNVName, "5_0"))
+		rom->dsa_version = 50;
+	else if (strstr(rom->header.VBNVName, "5_1")
+		 || strstr(rom->header.VBNVName, "u200_xdma_201820_1"))
+		rom->dsa_version = 51;
+	else if (strstr(rom->header.VBNVName, "5_2")
+		 || strstr(rom->header.VBNVName, "u200_xdma_201820_2")
+		 || strstr(rom->header.VBNVName, "u250_xdma_201820_1")
+		 || strstr(rom->header.VBNVName, "201830"))
+		rom->dsa_version = 52;
+	else if (strstr(rom->header.VBNVName, "5_3"))
+		rom->dsa_version = 53;
+
+	if (rom->header.FeatureBitMap & UNIFIED_PLATFORM)
+		rom->unified = true;
+
+	if (rom->header.FeatureBitMap & BOARD_MGMT_ENBLD)
+		rom->mb_mgmt_enabled = true;
+
+	if (rom->header.FeatureBitMap & MB_SCHEDULER)
+		rom->mb_sche_enabled = true;
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &rom_attr_group);
+	if (ret) {
+		xocl_err(&pdev->dev, "create sysfs failed");
+		goto failed;
+	}
+
+	tmp = rom->header.EntryPointString;
+	xocl_info(&pdev->dev, "ROM magic : %c%c%c%c",
+		tmp[0], tmp[1], tmp[2], tmp[3]);
+	xocl_info(&pdev->dev, "VBNV: %s", rom->header.VBNVName);
+	xocl_info(&pdev->dev, "DDR channel count : %d",
+		rom->header.DDRChannelCount);
+	xocl_info(&pdev->dev, "DDR channel size: %d GB",
+		rom->header.DDRChannelSize);
+	xocl_info(&pdev->dev, "Major Version: %d", rom->header.MajorVersion);
+	xocl_info(&pdev->dev, "Minor Version: %d", rom->header.MinorVersion);
+	xocl_info(&pdev->dev, "IPBuildID: %u", rom->header.IPBuildID);
+	xocl_info(&pdev->dev, "TimeSinceEpoch: %llx",
+		rom->header.TimeSinceEpoch);
+	xocl_info(&pdev->dev, "FeatureBitMap: %llx", rom->header.FeatureBitMap);
+
+	xocl_subdev_register(pdev, XOCL_SUBDEV_FEATURE_ROM, &rom_ops);
+	platform_set_drvdata(pdev, rom);
+
+	return 0;
+
+failed:
+	if (rom->base)
+		iounmap(rom->base);
+	devm_kfree(&pdev->dev, rom);
+	return ret;
+}
+
+static int feature_rom_remove(struct platform_device *pdev)
+{
+	struct feature_rom *rom;
+
+	xocl_info(&pdev->dev, "Remove feature rom");
+	rom = platform_get_drvdata(pdev);
+	if (!rom) {
+		xocl_err(&pdev->dev, "driver data is NULL");
+		return -EINVAL;
+	}
+	if (rom->base)
+		iounmap(rom->base);
+
+	sysfs_remove_group(&pdev->dev.kobj, &rom_attr_group);
+
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(&pdev->dev, rom);
+	return 0;
+}
+
+struct platform_device_id rom_id_table[] =  {
+	{ XOCL_FEATURE_ROM, 0 },
+	{ },
+};
+
+static struct platform_driver	feature_rom_driver = {
+	.probe		= feature_rom_probe,
+	.remove		= feature_rom_remove,
+	.driver		= {
+		.name = XOCL_FEATURE_ROM,
+	},
+	.id_table = rom_id_table,
+};
+
+int __init xocl_init_feature_rom(void)
+{
+	return platform_driver_register(&feature_rom_driver);
+}
+
+void xocl_fini_feature_rom(void)
+{
+	return platform_driver_unregister(&feature_rom_driver);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/firewall.c b/drivers/gpu/drm/xocl/subdev/firewall.c
new file mode 100644
index 000000000000..a32766507ae0
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/firewall.c
@@ -0,0 +1,389 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ *  Copyright (C) 2017-2019 Xilinx, Inc. All rights reserved.
+ *
+ *  Utility Functions for AXI firewall IP.
+ *  Author: Lizhi.Hou@Xilinx.com
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/ktime.h>
+#include <linux/rtc.h>
+#include "../xocl_drv.h"
+
+/* Firewall registers */
+#define	FAULT_STATUS				0x0
+#define	SOFT_CTRL				0x4
+#define	UNBLOCK_CTRL				0x8
+// Firewall error bits
+#define READ_RESPONSE_BUSY                        BIT(0)
+#define RECS_ARREADY_MAX_WAIT                     BIT(1)
+#define RECS_CONTINUOUS_RTRANSFERS_MAX_WAIT       BIT(2)
+#define ERRS_RDATA_NUM                            BIT(3)
+#define ERRS_RID                                  BIT(4)
+#define WRITE_RESPONSE_BUSY                       BIT(16)
+#define RECS_AWREADY_MAX_WAIT                     BIT(17)
+#define RECS_WREADY_MAX_WAIT                      BIT(18)
+#define RECS_WRITE_TO_BVALID_MAX_WAIT             BIT(19)
+#define ERRS_BRESP                                BIT(20)
+
+#define	FIREWALL_STATUS_BUSY	(READ_RESPONSE_BUSY | WRITE_RESPONSE_BUSY)
+#define	CLEAR_RESET_GPIO		0
+
+#define	READ_STATUS(fw, id)			\
+	XOCL_READ_REG32(fw->base_addrs[id] + FAULT_STATUS)
+#define	WRITE_UNBLOCK_CTRL(fw, id, val)			\
+	XOCL_WRITE_REG32(val, fw->base_addrs[id] + UNBLOCK_CTRL)
+
+#define	IS_FIRED(fw, id) (READ_STATUS(fw, id) & ~FIREWALL_STATUS_BUSY)
+
+#define	BUSY_RETRY_COUNT		20
+#define	BUSY_RETRY_INTERVAL		100		/* ms */
+#define	CLEAR_RETRY_COUNT		4
+#define	CLEAR_RETRY_INTERVAL		2		/* ms */
+
+#define	MAX_LEVEL		16
+
+struct firewall {
+	void __iomem		*base_addrs[MAX_LEVEL];
+	u32			max_level;
+	void __iomem		*gpio_addr;
+
+	u32			curr_status;
+	int			curr_level;
+
+	u32			err_detected_status;
+	u32			err_detected_level;
+	u64			err_detected_time;
+
+	bool			inject_firewall;
+};
+
+static int clear_firewall(struct platform_device *pdev);
+static u32 check_firewall(struct platform_device *pdev, int *level);
+
+static int get_prop(struct platform_device *pdev, u32 prop, void *val)
+{
+	struct firewall *fw;
+
+	fw = platform_get_drvdata(pdev);
+	BUG_ON(!fw);
+
+	check_firewall(pdev, NULL);
+	switch (prop) {
+	case XOCL_AF_PROP_TOTAL_LEVEL:
+		*(u32 *)val = fw->max_level;
+		break;
+	case XOCL_AF_PROP_STATUS:
+		*(u32 *)val = fw->curr_status;
+		break;
+	case XOCL_AF_PROP_LEVEL:
+		*(int *)val = fw->curr_level;
+		break;
+	case XOCL_AF_PROP_DETECTED_STATUS:
+		*(u32 *)val = fw->err_detected_status;
+		break;
+	case XOCL_AF_PROP_DETECTED_LEVEL:
+		*(u32 *)val = fw->err_detected_level;
+		break;
+	case XOCL_AF_PROP_DETECTED_TIME:
+		*(u64 *)val = fw->err_detected_time;
+		break;
+	default:
+		xocl_err(&pdev->dev, "Invalid prop %d", prop);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* sysfs support */
+static ssize_t show_firewall(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct firewall *fw;
+	u64 t;
+	u32 val;
+	int ret;
+
+	fw = platform_get_drvdata(pdev);
+	BUG_ON(!fw);
+
+	if (attr->index == XOCL_AF_PROP_DETECTED_TIME) {
+		get_prop(pdev,  attr->index, &t);
+		return sprintf(buf, "%llu\n", t);
+	}
+
+	ret = get_prop(pdev, attr->index, &val);
+	if (ret)
+		return 0;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static SENSOR_DEVICE_ATTR(status, 0444, show_firewall, NULL,
+	XOCL_AF_PROP_STATUS);
+static SENSOR_DEVICE_ATTR(level, 0444, show_firewall, NULL,
+	XOCL_AF_PROP_LEVEL);
+static SENSOR_DEVICE_ATTR(detected_status, 0444, show_firewall, NULL,
+	XOCL_AF_PROP_DETECTED_STATUS);
+static SENSOR_DEVICE_ATTR(detected_level, 0444, show_firewall, NULL,
+	XOCL_AF_PROP_DETECTED_LEVEL);
+static SENSOR_DEVICE_ATTR(detected_time, 0444, show_firewall, NULL,
+	XOCL_AF_PROP_DETECTED_TIME);
+
+static ssize_t clear_store(struct device *dev, struct device_attribute *da,
+	const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	u32 val = 0;
+
+	if (kstrtou32(buf, 10, &val) == -EINVAL || val != 1)
+		return -EINVAL;
+
+	clear_firewall(pdev);
+
+	return count;
+}
+static DEVICE_ATTR_WO(clear);
+
+static ssize_t inject_store(struct device *dev, struct device_attribute *da,
+	const char *buf, size_t count)
+{
+	struct firewall *fw = platform_get_drvdata(to_platform_device(dev));
+
+	fw->inject_firewall = true;
+	return count;
+}
+static DEVICE_ATTR_WO(inject);
+
+static struct attribute *firewall_attributes[] = {
+	&sensor_dev_attr_status.dev_attr.attr,
+	&sensor_dev_attr_level.dev_attr.attr,
+	&sensor_dev_attr_detected_status.dev_attr.attr,
+	&sensor_dev_attr_detected_level.dev_attr.attr,
+	&sensor_dev_attr_detected_time.dev_attr.attr,
+	&dev_attr_clear.attr,
+	&dev_attr_inject.attr,
+	NULL
+};
+
+static const struct attribute_group firewall_attrgroup = {
+	.attrs = firewall_attributes,
+};
+
+static u32 check_firewall(struct platform_device *pdev, int *level)
+{
+	struct firewall	*fw;
+//	struct timeval	time;
+	struct timespec64 now;
+	int	i;
+	u32	val = 0;
+
+	fw = platform_get_drvdata(pdev);
+	BUG_ON(!fw);
+
+	for (i = 0; i < fw->max_level; i++) {
+		val = IS_FIRED(fw, i);
+		if (val) {
+			xocl_info(&pdev->dev, "AXI Firewall %d tripped, "
+				  "status: 0x%x", i, val);
+			if (!fw->curr_status) {
+				fw->err_detected_status = val;
+				fw->err_detected_level = i;
+				ktime_get_ts64(&now);
+				fw->err_detected_time = (u64)(now.tv_sec -
+					(sys_tz.tz_minuteswest * 60));
+			}
+			fw->curr_level = i;
+
+			if (level)
+				*level = i;
+			break;
+		}
+	}
+
+	fw->curr_status = val;
+	fw->curr_level = i >= fw->max_level ? -1 : i;
+
+	/* Inject firewall for testing. */
+	if (fw->curr_level == -1 && fw->inject_firewall) {
+		fw->inject_firewall = false;
+		fw->curr_level = 0;
+		fw->curr_status = 0x1;
+	}
+
+	return fw->curr_status;
+}
+
+static int clear_firewall(struct platform_device *pdev)
+{
+	struct firewall	*fw;
+	int	i, retry = 0, clear_retry = 0;
+	u32	val;
+	int	ret = 0;
+
+	fw = platform_get_drvdata(pdev);
+	BUG_ON(!fw);
+
+	if (!check_firewall(pdev, NULL)) {
+		/* firewall is not tripped */
+		return 0;
+	}
+
+retry_level1:
+	for (i = 0; i < fw->max_level; i++) {
+		for (val = READ_STATUS(fw, i);
+			(val & FIREWALL_STATUS_BUSY) &&
+			retry++ < BUSY_RETRY_COUNT;
+			val = READ_STATUS(fw, i)) {
+			msleep(BUSY_RETRY_INTERVAL);
+		}
+		if (val & FIREWALL_STATUS_BUSY) {
+			xocl_err(&pdev->dev, "firewall %d busy", i);
+			ret = -EBUSY;
+			goto failed;
+		}
+		WRITE_UNBLOCK_CTRL(fw, i, 1);
+	}
+
+	if (check_firewall(pdev, NULL) && clear_retry++ < CLEAR_RETRY_COUNT) {
+		msleep(CLEAR_RETRY_INTERVAL);
+		goto retry_level1;
+	}
+
+	if (!check_firewall(pdev, NULL)) {
+		xocl_info(&pdev->dev, "firewall cleared level 1");
+		return 0;
+	}
+
+	clear_retry = 0;
+
+retry_level2:
+	XOCL_WRITE_REG32(CLEAR_RESET_GPIO, fw->gpio_addr);
+
+	if (check_firewall(pdev, NULL) && clear_retry++ < CLEAR_RETRY_COUNT) {
+		msleep(CLEAR_RETRY_INTERVAL);
+		goto retry_level2;
+	}
+
+	if (!check_firewall(pdev, NULL)) {
+		xocl_info(&pdev->dev, "firewall cleared level 2");
+		return 0;
+	}
+
+	xocl_info(&pdev->dev, "failed clear firewall, level %d, status 0x%x",
+		fw->curr_level, fw->curr_status);
+
+	ret = -EIO;
+
+failed:
+	return ret;
+}
+
+static struct xocl_firewall_funcs fw_ops = {
+	.clear_firewall	= clear_firewall,
+	.check_firewall = check_firewall,
+	.get_prop = get_prop,
+};
+
+static int firewall_remove(struct platform_device *pdev)
+{
+	struct firewall *fw;
+	int     i;
+
+	fw = platform_get_drvdata(pdev);
+	if (!fw) {
+		xocl_err(&pdev->dev, "driver data is NULL");
+		return -EINVAL;
+	}
+
+	sysfs_remove_group(&pdev->dev.kobj, &firewall_attrgroup);
+
+	for (i = 0; i <= fw->max_level; i++) {
+		if (fw->base_addrs[i])
+			iounmap(fw->base_addrs[i]);
+	}
+
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(&pdev->dev, fw);
+
+	return 0;
+}
+
+static int firewall_probe(struct platform_device *pdev)
+{
+	struct firewall	*fw;
+	struct resource	*res;
+	int	i, ret = 0;
+
+	xocl_info(&pdev->dev, "probe");
+
+	fw = devm_kzalloc(&pdev->dev, sizeof(*fw), GFP_KERNEL);
+	if (!fw)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, fw);
+
+	fw->curr_level = -1;
+
+	for (i = 0; i < MAX_LEVEL; i++) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		if (!res) {
+			fw->max_level = i - 1;
+			fw->gpio_addr = fw->base_addrs[i - 1];
+			break;
+		}
+		fw->base_addrs[i] =
+			ioremap_nocache(res->start, res->end - res->start + 1);
+		if (!fw->base_addrs[i]) {
+			ret = -EIO;
+			xocl_err(&pdev->dev, "Map iomem failed");
+			goto failed;
+		}
+	}
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &firewall_attrgroup);
+	if (ret) {
+		xocl_err(&pdev->dev, "create attr group failed: %d", ret);
+		goto failed;
+	}
+
+	xocl_subdev_register(pdev, XOCL_SUBDEV_AF, &fw_ops);
+
+	return 0;
+
+failed:
+	firewall_remove(pdev);
+	return ret;
+}
+
+struct platform_device_id firewall_id_table[] = {
+	{ XOCL_FIREWALL, 0 },
+	{ },
+};
+
+static struct platform_driver	firewall_driver = {
+	.probe		= firewall_probe,
+	.remove		= firewall_remove,
+	.driver		= {
+		.name = "xocl_firewall",
+	},
+	.id_table = firewall_id_table,
+};
+
+int __init xocl_init_firewall(void)
+{
+	return platform_driver_register(&firewall_driver);
+}
+
+void xocl_fini_firewall(void)
+{
+	return platform_driver_unregister(&firewall_driver);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/fmgr.c b/drivers/gpu/drm/xocl/subdev/fmgr.c
new file mode 100644
index 000000000000..99efd86ccd1b
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/fmgr.c
@@ -0,0 +1,198 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * FPGA Manager bindings for XRT driver
+ *
+ * Copyright (C) 2019 Xilinx, Inc. All rights reserved.
+ *
+ * Authors: Sonal Santan
+ *
+ */
+
+#include <linux/fpga/fpga-mgr.h>
+
+#include "../xocl_drv.h"
+#include "../xclbin.h"
+
+/*
+ * Container to capture and cache full xclbin as it is passed in blocks by FPGA
+ * Manager. xocl needs access to full xclbin to walk through xclbin sections. FPGA
+ * Manager's .write() backend sends incremental blocks without any knowledge of
+ * xclbin format forcing us to collect the blocks and stitch them together here.
+ * TODO:
+ * 1. Add a variant of API, icap_download_bitstream_axlf() which works off kernel buffer
+ * 2. Call this new API from FPGA Manager's write complete hook, xocl_pr_write_complete()
+ */
+
+struct xfpga_klass {
+	struct xocl_dev *xdev;
+	struct axlf *blob;
+	char name[64];
+	size_t count;
+	enum fpga_mgr_states state;
+};
+
+static int xocl_pr_write_init(struct fpga_manager *mgr,
+			      struct fpga_image_info *info, const char *buf, size_t count)
+{
+	struct xfpga_klass *obj = mgr->priv;
+	const struct axlf *bin = (const struct axlf *)buf;
+
+	if (count < sizeof(struct axlf)) {
+		obj->state = FPGA_MGR_STATE_WRITE_INIT_ERR;
+		return -EINVAL;
+	}
+
+	if (count > bin->m_header.m_length) {
+		obj->state = FPGA_MGR_STATE_WRITE_INIT_ERR;
+		return -EINVAL;
+	}
+
+	/* Free up the previous blob */
+	vfree(obj->blob);
+	obj->blob = vmalloc(bin->m_header.m_length);
+	if (!obj->blob) {
+		obj->state = FPGA_MGR_STATE_WRITE_INIT_ERR;
+		return -ENOMEM;
+	}
+
+	memcpy(obj->blob, buf, count);
+	xocl_info(&mgr->dev, "Begin download of xclbin %pUb of length %lld B", &obj->blob->m_header.uuid,
+		  obj->blob->m_header.m_length);
+	obj->count = count;
+	obj->state = FPGA_MGR_STATE_WRITE_INIT;
+	return 0;
+}
+
+static int xocl_pr_write(struct fpga_manager *mgr,
+			 const char *buf, size_t count)
+{
+	struct xfpga_klass *obj = mgr->priv;
+	char *curr = (char *)obj->blob;
+
+	if ((obj->state != FPGA_MGR_STATE_WRITE_INIT) && (obj->state != FPGA_MGR_STATE_WRITE)) {
+		obj->state = FPGA_MGR_STATE_WRITE_ERR;
+		return -EINVAL;
+	}
+
+	curr += obj->count;
+	obj->count += count;
+	/* Check if the xclbin buffer is not longer than advertised in the header */
+	if (obj->blob->m_header.m_length < obj->count) {
+		obj->state = FPGA_MGR_STATE_WRITE_ERR;
+		return -EINVAL;
+	}
+	memcpy(curr, buf, count);
+	xocl_info(&mgr->dev, "Next block of %zu B of xclbin %pUb", count, &obj->blob->m_header.uuid);
+	obj->state = FPGA_MGR_STATE_WRITE;
+	return 0;
+}
+
+
+static int xocl_pr_write_complete(struct fpga_manager *mgr,
+				  struct fpga_image_info *info)
+{
+	int result;
+	struct xfpga_klass *obj = mgr->priv;
+
+	if (obj->state != FPGA_MGR_STATE_WRITE) {
+		obj->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
+		return -EINVAL;
+	}
+
+	/* Check if we got the complete xclbin */
+	if (obj->blob->m_header.m_length != obj->count) {
+		obj->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
+		return -EINVAL;
+	}
+	/* Send the xclbin blob to actual download framework in icap */
+	result = xocl_icap_download_axlf(obj->xdev, obj->blob);
+	obj->state = result ? FPGA_MGR_STATE_WRITE_COMPLETE_ERR : FPGA_MGR_STATE_WRITE_COMPLETE;
+	xocl_info(&mgr->dev, "Finish download of xclbin %pUb of size %zu B", &obj->blob->m_header.uuid, obj->count);
+	vfree(obj->blob);
+	obj->blob = NULL;
+	obj->count = 0;
+	return result;
+}
+
+static enum fpga_mgr_states xocl_pr_state(struct fpga_manager *mgr)
+{
+	struct xfpga_klass *obj = mgr->priv;
+
+	return obj->state;
+}
+
+static const struct fpga_manager_ops xocl_pr_ops = {
+	.initial_header_size = sizeof(struct axlf),
+	.write_init = xocl_pr_write_init,
+	.write = xocl_pr_write,
+	.write_complete = xocl_pr_write_complete,
+	.state = xocl_pr_state,
+};
+
+
+struct platform_device_id fmgr_id_table[] = {
+	{ XOCL_FMGR, 0 },
+	{ },
+};
+
+static int fmgr_probe(struct platform_device *pdev)
+{
+	struct fpga_manager *mgr;
+	int ret = 0;
+	struct xfpga_klass *obj = kzalloc(sizeof(struct xfpga_klass), GFP_KERNEL);
+
+	if (!obj)
+		return -ENOMEM;
+
+	obj->xdev = xocl_get_xdev(pdev);
+	snprintf(obj->name, sizeof(obj->name), "Xilinx PCIe FPGA Manager");
+
+	obj->state = FPGA_MGR_STATE_UNKNOWN;
+	mgr = fpga_mgr_create(&pdev->dev, obj->name, &xocl_pr_ops, obj);
+	if (!mgr) {
+		ret = -ENODEV;
+		goto out;
+	}
+	ret = fpga_mgr_register(mgr);
+	if (ret)
+		goto out;
+
+	return ret;
+out:
+	kfree(obj);
+	return ret;
+}
+
+static int fmgr_remove(struct platform_device *pdev)
+{
+	struct fpga_manager *mgr = platform_get_drvdata(pdev);
+	struct xfpga_klass *obj = mgr->priv;
+
+	obj->state = FPGA_MGR_STATE_UNKNOWN;
+	fpga_mgr_unregister(mgr);
+
+	platform_set_drvdata(pdev, NULL);
+	vfree(obj->blob);
+	kfree(obj);
+	return 0;
+}
+
+static struct platform_driver	fmgr_driver = {
+	.probe		= fmgr_probe,
+	.remove		= fmgr_remove,
+	.driver		= {
+		.name = "xocl_fmgr",
+	},
+	.id_table = fmgr_id_table,
+};
+
+int __init xocl_init_fmgr(void)
+{
+	return platform_driver_register(&fmgr_driver);
+}
+
+void xocl_fini_fmgr(void)
+{
+	platform_driver_unregister(&fmgr_driver);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/icap.c b/drivers/gpu/drm/xocl/subdev/icap.c
new file mode 100644
index 000000000000..93eb6265a9c4
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/icap.c
@@ -0,0 +1,2859 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ *  Copyright (C) 2017 Xilinx, Inc. All rights reserved.
+ *  Author: Sonal Santan
+ *  Code copied verbatim from SDAccel xcldma kernel mode driver
+ *
+ */
+
+/*
+ * TODO: Currently, locking / unlocking bitstream is implemented w/ pid as
+ * identification of bitstream users. We assume that, on bare metal, an app
+ * has only one process and will open both user and mgmt pfs. In this model,
+ * xclmgmt has enough information to handle locking/unlocking alone, but we
+ * still involve user pf and mailbox here so that it'll be easier to support
+ * cloud env later. We'll replace pid with a token that is more appropriate
+ * to identify a user later as well.
+ */
+
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <linux/uuid.h>
+#include <linux/pid.h>
+#include "../xclbin.h"
+#include "../xocl_drv.h"
+#include <drm/xmgmt_drm.h>
+
+#if defined(XOCL_UUID)
+static uuid_t uuid_null = NULL_UUID_LE;
+#endif
+
+#define	ICAP_ERR(icap, fmt, arg...)	\
+	xocl_err(&(icap)->icap_pdev->dev, fmt "\n", ##arg)
+#define	ICAP_INFO(icap, fmt, arg...)	\
+	xocl_info(&(icap)->icap_pdev->dev, fmt "\n", ##arg)
+#define	ICAP_DBG(icap, fmt, arg...)	\
+	xocl_dbg(&(icap)->icap_pdev->dev, fmt "\n", ##arg)
+
+#define	ICAP_PRIVILEGED(icap)	((icap)->icap_regs != NULL)
+#define DMA_HWICAP_BITFILE_BUFFER_SIZE 1024
+#define	ICAP_MAX_REG_GROUPS		ARRAY_SIZE(XOCL_RES_ICAP_MGMT)
+
+#define	ICAP_MAX_NUM_CLOCKS		2
+#define OCL_CLKWIZ_STATUS_OFFSET	0x4
+#define OCL_CLKWIZ_CONFIG_OFFSET(n)	(0x200 + 4 * (n))
+#define OCL_CLK_FREQ_COUNTER_OFFSET	0x8
+
+/*
+ * Bitstream header information.
+ */
+struct XHwIcap_Bit_Header {
+	unsigned int HeaderLength;     /* Length of header in 32 bit words */
+	unsigned int BitstreamLength;  /* Length of bitstream to read in bytes*/
+	unsigned char *DesignName;     /* Design name read from bitstream header */
+	unsigned char *PartName;       /* Part name read from bitstream header */
+	unsigned char *Date;           /* Date read from bitstream header */
+	unsigned char *Time;           /* Bitstream creation time read from header */
+	unsigned int MagicLength;      /* Length of the magic numbers in header */
+};
+
+#define XHI_BIT_HEADER_FAILURE	-1
+/* Used for parsing bitstream header */
+#define XHI_EVEN_MAGIC_BYTE	0x0f
+#define XHI_ODD_MAGIC_BYTE	0xf0
+/* Extra mode for IDLE */
+#define XHI_OP_IDLE		-1
+/* The imaginary module length register */
+#define XHI_MLR			15
+
+#define	GATE_FREEZE_USER	0x0c
+#define GATE_FREEZE_SHELL	0x00
+
+static u32 gate_free_user[] = {0xe, 0xc, 0xe, 0xf};
+static u32 gate_free_shell[] = {0x8, 0xc, 0xe, 0xf};
+
+/*
+ * AXI-HWICAP IP register layout
+ */
+struct icap_reg {
+	u32			ir_rsvd1[7];
+	u32			ir_gier;
+	u32			ir_isr;
+	u32			ir_rsvd2;
+	u32			ir_ier;
+	u32			ir_rsvd3[53];
+	u32			ir_wf;
+	u32			ir_rf;
+	u32			ir_sz;
+	u32			ir_cr;
+	u32			ir_sr;
+	u32			ir_wfv;
+	u32			ir_rfo;
+	u32			ir_asr;
+} __attribute__((packed));
+
+struct icap_generic_state {
+	u32			igs_state;
+} __attribute__((packed));
+
+struct icap_axi_gate {
+	u32			iag_wr;
+	u32			iag_rvsd;
+	u32			iag_rd;
+} __attribute__((packed));
+
+struct icap_bitstream_user {
+	struct list_head	ibu_list;
+	pid_t			ibu_pid;
+};
+
+struct icap {
+	struct platform_device	*icap_pdev;
+	struct mutex		icap_lock;
+	struct icap_reg		*icap_regs;
+	struct icap_generic_state *icap_state;
+	unsigned int            idcode;
+	bool			icap_axi_gate_frozen;
+	bool			icap_axi_gate_shell_frozen;
+	struct icap_axi_gate	*icap_axi_gate;
+
+	u64			icap_bitstream_id;
+	uuid_t			icap_bitstream_uuid;
+	int			icap_bitstream_ref;
+	struct list_head	icap_bitstream_users;
+
+	char			*icap_clear_bitstream;
+	unsigned long		icap_clear_bitstream_length;
+
+	char			*icap_clock_bases[ICAP_MAX_NUM_CLOCKS];
+	unsigned short		icap_ocl_frequency[ICAP_MAX_NUM_CLOCKS];
+
+	char                    *icap_clock_freq_topology;
+	unsigned long		icap_clock_freq_topology_length;
+	char                    *icap_clock_freq_counter;
+	struct mem_topology      *mem_topo;
+	struct ip_layout         *ip_layout;
+	struct debug_ip_layout   *debug_layout;
+	struct connectivity      *connectivity;
+
+	char			*bit_buffer;
+	unsigned long		bit_length;
+};
+
+static inline u32 reg_rd(void __iomem *reg)
+{
+	return XOCL_READ_REG32(reg);
+}
+
+static inline void reg_wr(void __iomem *reg, u32 val)
+{
+	iowrite32(val, reg);
+}
+
+/*
+ * Precomputed table with config0 and config2 register values together with
+ * target frequency. The steps are approximately 5 MHz apart. Table is
+ * generated by wiz.pl.
+ */
+const static struct xclmgmt_ocl_clockwiz {
+	/* target frequency */
+	unsigned short ocl;
+	/* config0 register */
+	unsigned long config0;
+	/* config2 register */
+	unsigned short config2;
+} frequency_table[] = {
+	{/* 600*/   60, 0x0601, 0x000a},
+	{/* 600*/   66, 0x0601, 0x0009},
+	{/* 600*/   75, 0x0601, 0x0008},
+	{/* 800*/   80, 0x0801, 0x000a},
+	{/* 600*/   85, 0x0601, 0x0007},
+	{/* 900*/   90, 0x0901, 0x000a},
+	{/*1000*/  100, 0x0a01, 0x000a},
+	{/*1100*/  110, 0x0b01, 0x000a},
+	{/* 700*/  116, 0x0701, 0x0006},
+	{/*1100*/  122, 0x0b01, 0x0009},
+	{/* 900*/  128, 0x0901, 0x0007},
+	{/*1200*/  133, 0x0c01, 0x0009},
+	{/*1400*/  140, 0x0e01, 0x000a},
+	{/*1200*/  150, 0x0c01, 0x0008},
+	{/*1400*/  155, 0x0e01, 0x0009},
+	{/* 800*/  160, 0x0801, 0x0005},
+	{/*1000*/  166, 0x0a01, 0x0006},
+	{/*1200*/  171, 0x0c01, 0x0007},
+	{/* 900*/  180, 0x0901, 0x0005},
+	{/*1300*/  185, 0x0d01, 0x0007},
+	{/*1400*/  200, 0x0e01, 0x0007},
+	{/*1300*/  216, 0x0d01, 0x0006},
+	{/* 900*/  225, 0x0901, 0x0004},
+	{/*1400*/  233, 0x0e01, 0x0006},
+	{/*1200*/  240, 0x0c01, 0x0005},
+	{/*1000*/  250, 0x0a01, 0x0004},
+	{/*1300*/  260, 0x0d01, 0x0005},
+	{/* 800*/  266, 0x0801, 0x0003},
+	{/*1100*/  275, 0x0b01, 0x0004},
+	{/*1400*/  280, 0x0e01, 0x0005},
+	{/*1200*/  300, 0x0c01, 0x0004},
+	{/*1300*/  325, 0x0d01, 0x0004},
+	{/*1000*/  333, 0x0a01, 0x0003},
+	{/*1400*/  350, 0x0e01, 0x0004},
+	{/*1100*/  366, 0x0b01, 0x0003},
+	{/*1200*/  400, 0x0c01, 0x0003},
+	{/*1300*/  433, 0x0d01, 0x0003},
+	{/* 900*/  450, 0x0901, 0x0002},
+	{/*1400*/  466, 0x0e01, 0x0003},
+	{/*1000*/  500, 0x0a01, 0x0002}
+};
+
+static int icap_verify_bitstream_axlf(struct platform_device *pdev,
+	struct axlf *xclbin);
+static int icap_parse_bitstream_axlf_section(struct platform_device *pdev,
+	const struct axlf *xclbin, enum axlf_section_kind kind);
+
+static struct icap_bitstream_user *alloc_user(pid_t pid)
+{
+	struct icap_bitstream_user *u =
+		kzalloc(sizeof(struct icap_bitstream_user), GFP_KERNEL);
+
+	if (u) {
+		INIT_LIST_HEAD(&u->ibu_list);
+		u->ibu_pid = pid;
+	}
+	return u;
+}
+
+static void free_user(struct icap_bitstream_user *u)
+{
+	kfree(u);
+}
+
+static struct icap_bitstream_user *obtain_user(struct icap *icap, pid_t pid)
+{
+	struct list_head *pos, *n;
+
+	list_for_each_safe(pos, n, &icap->icap_bitstream_users) {
+		struct icap_bitstream_user *u = list_entry(pos, struct icap_bitstream_user, ibu_list);
+
+		if (u->ibu_pid == pid)
+			return u;
+	}
+
+	return NULL;
+}
+
+static void icap_read_from_peer(struct platform_device *pdev, enum data_kind kind, void *resp, size_t resplen)
+{
+	struct mailbox_subdev_peer subdev_peer = {0};
+	size_t data_len = sizeof(struct mailbox_subdev_peer);
+	struct mailbox_req *mb_req = NULL;
+	size_t reqlen = sizeof(struct mailbox_req) + data_len;
+
+	mb_req = vmalloc(reqlen);
+	if (!mb_req)
+		return;
+
+	mb_req->req = MAILBOX_REQ_PEER_DATA;
+
+	subdev_peer.kind = kind;
+	memcpy(mb_req->data, &subdev_peer, data_len);
+
+	(void) xocl_peer_request(XOCL_PL_DEV_TO_XDEV(pdev),
+		mb_req, reqlen, resp, &resplen, NULL, NULL);
+
+	vfree(mb_req);
+}
+
+
+static int add_user(struct icap *icap, pid_t pid)
+{
+	struct icap_bitstream_user *u;
+
+	u = obtain_user(icap, pid);
+	if (u)
+		return 0;
+
+	u = alloc_user(pid);
+	if (!u)
+		return -ENOMEM;
+
+	list_add_tail(&u->ibu_list, &icap->icap_bitstream_users);
+	icap->icap_bitstream_ref++;
+	return 0;
+}
+
+static int del_user(struct icap *icap, pid_t pid)
+{
+	struct icap_bitstream_user *u = NULL;
+
+	u = obtain_user(icap, pid);
+	if (!u)
+		return -EINVAL;
+
+	list_del(&u->ibu_list);
+	free_user(u);
+	icap->icap_bitstream_ref--;
+	return 0;
+}
+
+static void del_all_users(struct icap *icap)
+{
+	struct icap_bitstream_user *u = NULL;
+	struct list_head *pos, *n;
+
+	if (icap->icap_bitstream_ref == 0)
+		return;
+
+	list_for_each_safe(pos, n, &icap->icap_bitstream_users) {
+		u = list_entry(pos, struct icap_bitstream_user, ibu_list);
+		list_del(&u->ibu_list);
+		free_user(u);
+	}
+
+	ICAP_INFO(icap, "removed %d users", icap->icap_bitstream_ref);
+	icap->icap_bitstream_ref = 0;
+}
+
+static unsigned int find_matching_freq_config(unsigned int freq)
+{
+	unsigned int start = 0;
+	unsigned int end = ARRAY_SIZE(frequency_table) - 1;
+	unsigned int idx = ARRAY_SIZE(frequency_table) - 1;
+
+	if (freq < frequency_table[0].ocl)
+		return 0;
+
+	if (freq > frequency_table[ARRAY_SIZE(frequency_table) - 1].ocl)
+		return ARRAY_SIZE(frequency_table) - 1;
+
+	while (start < end) {
+		if (freq == frequency_table[idx].ocl)
+			break;
+		if (freq < frequency_table[idx].ocl)
+			end = idx;
+		else
+			start = idx + 1;
+		idx = start + (end - start) / 2;
+	}
+	if (freq < frequency_table[idx].ocl)
+		idx--;
+
+	return idx;
+}
+
+static unsigned short icap_get_ocl_frequency(const struct icap *icap, int idx)
+{
+#define XCL_INPUT_FREQ 100
+	const u64 input = XCL_INPUT_FREQ;
+	u32 val;
+	u32 mul0, div0;
+	u32 mul_frac0 = 0;
+	u32 div1;
+	u32 div_frac1 = 0;
+	u64 freq;
+	char *base = NULL;
+
+	if (ICAP_PRIVILEGED(icap)) {
+		base = icap->icap_clock_bases[idx];
+	  val = reg_rd(base + OCL_CLKWIZ_STATUS_OFFSET);
+		if ((val & 1) == 0)
+			return 0;
+
+		val = reg_rd(base + OCL_CLKWIZ_CONFIG_OFFSET(0));
+
+		div0 = val & 0xff;
+		mul0 = (val & 0xff00) >> 8;
+		if (val & BIT(26)) {
+			mul_frac0 = val >> 16;
+			mul_frac0 &= 0x3ff;
+		}
+
+		/*
+		 * Multiply both numerator (mul0) and the denominator (div0) with 1000
+		 * to account for fractional portion of multiplier
+		 */
+		mul0 *= 1000;
+		mul0 += mul_frac0;
+		div0 *= 1000;
+
+		val = reg_rd(base + OCL_CLKWIZ_CONFIG_OFFSET(2));
+
+		div1 = val & 0xff;
+		if (val & BIT(18)) {
+			div_frac1 = val >> 8;
+			div_frac1 &= 0x3ff;
+		}
+
+		/*
+		 * Multiply both numerator (mul0) and the denominator (div1) with 1000 to
+		 * account for fractional portion of divider
+		 */
+
+		div1 *= 1000;
+		div1 += div_frac1;
+		div0 *= div1;
+		mul0 *= 1000;
+		if (div0 == 0) {
+			ICAP_ERR(icap, "clockwiz 0 divider");
+			return 0;
+		}
+		freq = (input * mul0) / div0;
+	} else {
+		icap_read_from_peer(icap->icap_pdev, CLOCK_FREQ_0, (u32 *)&freq, sizeof(u32));
+	}
+	return freq;
+}
+
+static unsigned int icap_get_clock_frequency_counter_khz(const struct icap *icap, int idx)
+{
+	u32 freq, status;
+	char *base = icap->icap_clock_freq_counter;
+	int times;
+
+	times = 10;
+	freq = 0;
+	/*
+	 * reset and wait until done
+	 */
+	if (ICAP_PRIVILEGED(icap)) {
+		if (uuid_is_null(&icap->icap_bitstream_uuid)) {
+			ICAP_ERR(icap, "ERROR: There isn't a xclbin loaded in the dynamic "
+				 "region, frequencies counter cannot be determined");
+			return freq;
+		}
+		reg_wr(base, 0x1);
+
+		while (times != 0) {
+			status = reg_rd(base);
+			if (status == 0x2)
+				break;
+			mdelay(1);
+			times--;
+		};
+
+	  freq = reg_rd(base + OCL_CLK_FREQ_COUNTER_OFFSET + idx * sizeof(u32));
+	} else {
+		icap_read_from_peer(icap->icap_pdev, FREQ_COUNTER_0, (u32 *)&freq, sizeof(u32));
+	}
+	return freq;
+}
+/*
+ * Based on Clocking Wizard v5.1, section Dynamic Reconfiguration
+ * through AXI4-Lite
+ */
+static int icap_ocl_freqscaling(struct icap *icap, bool force)
+{
+	unsigned int curr_freq;
+	u32 config;
+	int i;
+	int j = 0;
+	u32 val = 0;
+	unsigned int idx = 0;
+	long err = 0;
+
+	for (i = 0; i < ICAP_MAX_NUM_CLOCKS; ++i) {
+		// A value of zero means skip scaling for this clock index
+		if (!icap->icap_ocl_frequency[i])
+			continue;
+
+		idx = find_matching_freq_config(icap->icap_ocl_frequency[i]);
+		curr_freq = icap_get_ocl_frequency(icap, i);
+		ICAP_INFO(icap, "Clock %d, Current %d Mhz, New %d Mhz ",
+				i, curr_freq, icap->icap_ocl_frequency[i]);
+
+		/*
+		 * If current frequency is in the same step as the
+		 * requested frequency then nothing to do.
+		 */
+		if (!force && (find_matching_freq_config(curr_freq) == idx))
+			continue;
+
+		val = reg_rd(icap->icap_clock_bases[i] +
+			OCL_CLKWIZ_STATUS_OFFSET);
+		if (val != 1) {
+			ICAP_ERR(icap, "clockwiz %d is busy", i);
+			err = -EBUSY;
+			break;
+		}
+
+		config = frequency_table[idx].config0;
+		reg_wr(icap->icap_clock_bases[i] + OCL_CLKWIZ_CONFIG_OFFSET(0),
+			config);
+		config = frequency_table[idx].config2;
+		reg_wr(icap->icap_clock_bases[i] + OCL_CLKWIZ_CONFIG_OFFSET(2),
+			config);
+		msleep(10);
+		reg_wr(icap->icap_clock_bases[i] + OCL_CLKWIZ_CONFIG_OFFSET(23),
+			0x00000007);
+		msleep(1);
+		reg_wr(icap->icap_clock_bases[i] + OCL_CLKWIZ_CONFIG_OFFSET(23),
+			0x00000002);
+
+		ICAP_INFO(icap, "clockwiz waiting for locked signal");
+		msleep(100);
+		for (j = 0; j < 100; j++) {
+			val = reg_rd(icap->icap_clock_bases[i] +
+				OCL_CLKWIZ_STATUS_OFFSET);
+			if (val != 1) {
+				msleep(100);
+				continue;
+			}
+		}
+		if (val != 1) {
+			ICAP_ERR(icap, "clockwiz MMCM/PLL did not lock after %d ms, "
+				"restoring the original configuration", 100 * 100);
+			/* restore the original clock configuration */
+			reg_wr(icap->icap_clock_bases[i] +
+				OCL_CLKWIZ_CONFIG_OFFSET(23), 0x00000004);
+			msleep(10);
+			reg_wr(icap->icap_clock_bases[i] +
+				OCL_CLKWIZ_CONFIG_OFFSET(23), 0x00000000);
+			err = -ETIMEDOUT;
+			break;
+		}
+		val = reg_rd(icap->icap_clock_bases[i] +
+			OCL_CLKWIZ_CONFIG_OFFSET(0));
+		ICAP_INFO(icap, "clockwiz CONFIG(0) 0x%x", val);
+		val = reg_rd(icap->icap_clock_bases[i] +
+			OCL_CLKWIZ_CONFIG_OFFSET(2));
+		ICAP_INFO(icap, "clockwiz CONFIG(2) 0x%x", val);
+	}
+
+	return err;
+}
+
+static bool icap_bitstream_in_use(struct icap *icap, pid_t pid)
+{
+	BUG_ON(icap->icap_bitstream_ref < 0);
+
+	/* Any user counts if pid isn't specified. */
+	if (pid == 0)
+		return icap->icap_bitstream_ref != 0;
+
+	if (icap->icap_bitstream_ref == 0)
+		return false;
+	if ((icap->icap_bitstream_ref == 1) && obtain_user(icap, pid))
+		return false;
+	return true;
+}
+
+static int icap_freeze_axi_gate_shell(struct icap *icap)
+{
+	xdev_handle_t xdev = xocl_get_xdev(icap->icap_pdev);
+
+	ICAP_INFO(icap, "freezing Shell AXI gate");
+	BUG_ON(icap->icap_axi_gate_shell_frozen);
+
+	(void) reg_rd(&icap->icap_axi_gate->iag_rd);
+	reg_wr(&icap->icap_axi_gate->iag_wr, GATE_FREEZE_SHELL);
+	(void) reg_rd(&icap->icap_axi_gate->iag_rd);
+
+	if (!xocl_is_unified(xdev)) {
+		reg_wr(&icap->icap_regs->ir_cr, 0xc);
+		ndelay(20);
+	} else {
+		/* New ICAP reset sequence applicable only to unified dsa. */
+		reg_wr(&icap->icap_regs->ir_cr, 0x8);
+		ndelay(2000);
+		reg_wr(&icap->icap_regs->ir_cr, 0x0);
+		ndelay(2000);
+		reg_wr(&icap->icap_regs->ir_cr, 0x4);
+		ndelay(2000);
+		reg_wr(&icap->icap_regs->ir_cr, 0x0);
+		ndelay(2000);
+	}
+
+	icap->icap_axi_gate_shell_frozen = true;
+
+	return 0;
+}
+
+static int icap_free_axi_gate_shell(struct icap *icap)
+{
+	int i;
+
+	ICAP_INFO(icap, "freeing Shell AXI gate");
+	/*
+	 * First pulse the OCL RESET. This is important for PR with multiple
+	 * clocks as it resets the edge triggered clock converter FIFO
+	 */
+
+	if (!icap->icap_axi_gate_shell_frozen)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(gate_free_shell); i++) {
+		(void) reg_rd(&icap->icap_axi_gate->iag_rd);
+		reg_wr(&icap->icap_axi_gate->iag_wr, gate_free_shell[i]);
+		mdelay(50);
+	}
+
+	(void) reg_rd(&icap->icap_axi_gate->iag_rd);
+
+	icap->icap_axi_gate_shell_frozen = false;
+
+	return 0;
+}
+
+static int icap_freeze_axi_gate(struct icap *icap)
+{
+	xdev_handle_t xdev = xocl_get_xdev(icap->icap_pdev);
+
+	ICAP_INFO(icap, "freezing CL AXI gate");
+	BUG_ON(icap->icap_axi_gate_frozen);
+
+	(void) reg_rd(&icap->icap_axi_gate->iag_rd);
+	reg_wr(&icap->icap_axi_gate->iag_wr, GATE_FREEZE_USER);
+	(void) reg_rd(&icap->icap_axi_gate->iag_rd);
+
+	if (!xocl_is_unified(xdev)) {
+		reg_wr(&icap->icap_regs->ir_cr, 0xc);
+		ndelay(20);
+	} else {
+		/* New ICAP reset sequence applicable only to unified dsa. */
+		reg_wr(&icap->icap_regs->ir_cr, 0x8);
+		ndelay(2000);
+		reg_wr(&icap->icap_regs->ir_cr, 0x0);
+		ndelay(2000);
+		reg_wr(&icap->icap_regs->ir_cr, 0x4);
+		ndelay(2000);
+		reg_wr(&icap->icap_regs->ir_cr, 0x0);
+		ndelay(2000);
+	}
+
+	icap->icap_axi_gate_frozen = true;
+
+	return 0;
+}
+
+static int icap_free_axi_gate(struct icap *icap)
+{
+	int i;
+
+	ICAP_INFO(icap, "freeing CL AXI gate");
+	/*
+	 * First pulse the OCL RESET. This is important for PR with multiple
+	 * clocks as it resets the edge triggered clock converter FIFO
+	 */
+
+	if (!icap->icap_axi_gate_frozen)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(gate_free_user); i++) {
+		(void) reg_rd(&icap->icap_axi_gate->iag_rd);
+		reg_wr(&icap->icap_axi_gate->iag_wr, gate_free_user[i]);
+		ndelay(500);
+	}
+
+	(void) reg_rd(&icap->icap_axi_gate->iag_rd);
+
+	icap->icap_axi_gate_frozen = false;
+
+	return 0;
+}
+
+static void platform_reset_axi_gate(struct platform_device *pdev)
+{
+	struct icap *icap = platform_get_drvdata(pdev);
+
+	/* Can only be done from mgmt pf. */
+	if (!ICAP_PRIVILEGED(icap))
+		return;
+
+	mutex_lock(&icap->icap_lock);
+	if (!icap_bitstream_in_use(icap, 0)) {
+		(void) icap_freeze_axi_gate(platform_get_drvdata(pdev));
+		msleep(500);
+		(void) icap_free_axi_gate(platform_get_drvdata(pdev));
+		msleep(500);
+	}
+	mutex_unlock(&icap->icap_lock);
+}
+
+static int set_freqs(struct icap *icap, unsigned short *freqs, int num_freqs)
+{
+	int i;
+	int err;
+	u32 val;
+
+	for (i = 0; i < min(ICAP_MAX_NUM_CLOCKS, num_freqs); ++i) {
+		if (freqs[i] == 0)
+			continue;
+
+		val = reg_rd(icap->icap_clock_bases[i] +
+			OCL_CLKWIZ_STATUS_OFFSET);
+		if ((val & 0x1) == 0) {
+			ICAP_ERR(icap, "clockwiz %d is busy", i);
+			err = -EBUSY;
+			goto done;
+		}
+	}
+
+	memcpy(icap->icap_ocl_frequency, freqs,
+		sizeof(*freqs) * min(ICAP_MAX_NUM_CLOCKS, num_freqs));
+
+	icap_freeze_axi_gate(icap);
+	err = icap_ocl_freqscaling(icap, false);
+	icap_free_axi_gate(icap);
+
+done:
+	return err;
+
+}
+
+static int set_and_verify_freqs(struct icap *icap, unsigned short *freqs, int num_freqs)
+{
+	int i;
+	int err;
+	u32 clock_freq_counter, request_in_khz, tolerance;
+
+	err = set_freqs(icap, freqs, num_freqs);
+	if (err)
+		return err;
+
+	for (i = 0; i < min(ICAP_MAX_NUM_CLOCKS, num_freqs); ++i) {
+		if (!freqs[i])
+			continue;
+		clock_freq_counter = icap_get_clock_frequency_counter_khz(icap, i);
+		if (clock_freq_counter == 0) {
+			err = -EDOM;
+			break;
+		}
+		request_in_khz = freqs[i]*1000;
+		tolerance = freqs[i]*50;
+		if (tolerance < abs(clock_freq_counter-request_in_khz)) {
+			ICAP_ERR(icap, "Frequency is higher than tolerance value, request %u khz, "
+				 "actual %u khz", request_in_khz, clock_freq_counter);
+			err = -EDOM;
+			break;
+		}
+	}
+
+	return err;
+}
+
+static int icap_ocl_set_freqscaling(struct platform_device *pdev,
+	unsigned int region, unsigned short *freqs, int num_freqs)
+{
+	struct icap *icap = platform_get_drvdata(pdev);
+	int err = 0;
+
+	/* Can only be done from mgmt pf. */
+	if (!ICAP_PRIVILEGED(icap))
+		return -EPERM;
+
+	/* For now, only PR region 0 is supported. */
+	if (region != 0)
+		return -EINVAL;
+
+	mutex_lock(&icap->icap_lock);
+
+	err = set_freqs(icap, freqs, num_freqs);
+
+	mutex_unlock(&icap->icap_lock);
+
+	return err;
+}
+
+static int icap_ocl_update_clock_freq_topology(struct platform_device *pdev, struct xclmgmt_ioc_freqscaling *freq_obj)
+{
+	struct icap *icap = platform_get_drvdata(pdev);
+	struct clock_freq_topology *topology = 0;
+	int num_clocks = 0;
+	int i = 0;
+	int err = 0;
+
+	mutex_lock(&icap->icap_lock);
+	if (icap->icap_clock_freq_topology) {
+		topology = (struct clock_freq_topology *)icap->icap_clock_freq_topology;
+		num_clocks = topology->m_count;
+		ICAP_INFO(icap, "Num clocks is %d", num_clocks);
+		for (i = 0; i < ARRAY_SIZE(freq_obj->ocl_target_freq); i++) {
+			ICAP_INFO(icap, "requested frequency is : "
+				"%d xclbin freq is: %d",
+				freq_obj->ocl_target_freq[i],
+				topology->m_clock_freq[i].m_freq_Mhz);
+			if (freq_obj->ocl_target_freq[i] >
+				topology->m_clock_freq[i].m_freq_Mhz) {
+				ICAP_ERR(icap, "Unable to set frequency as "
+					"requested frequency %d is greater "
+					"than set by xclbin %d",
+					freq_obj->ocl_target_freq[i],
+					topology->m_clock_freq[i].m_freq_Mhz);
+				err = -EDOM;
+				goto done;
+			}
+		}
+	} else {
+		ICAP_ERR(icap, "ERROR: There isn't a hardware accelerator loaded in the dynamic region."
+			" Validation of accelerator frequencies cannot be determine");
+		err = -EDOM;
+		goto done;
+	}
+
+	err = set_and_verify_freqs(icap, freq_obj->ocl_target_freq, ARRAY_SIZE(freq_obj->ocl_target_freq));
+
+done:
+	mutex_unlock(&icap->icap_lock);
+	return err;
+}
+
+static int icap_ocl_get_freqscaling(struct platform_device *pdev,
+	unsigned int region, unsigned short *freqs, int num_freqs)
+{
+	int i;
+	struct icap *icap = platform_get_drvdata(pdev);
+
+	/* For now, only PR region 0 is supported. */
+	if (region != 0)
+		return -EINVAL;
+
+	mutex_lock(&icap->icap_lock);
+	for (i = 0; i < min(ICAP_MAX_NUM_CLOCKS, num_freqs); i++)
+		freqs[i] = icap_get_ocl_frequency(icap, i);
+	mutex_unlock(&icap->icap_lock);
+
+	return 0;
+}
+
+static inline bool mig_calibration_done(struct icap *icap)
+{
+	return (reg_rd(&icap->icap_state->igs_state) & BIT(0)) != 0;
+}
+
+/* Check for MIG calibration. */
+static int calibrate_mig(struct icap *icap)
+{
+	int i;
+
+	for (i = 0; i < 10 && !mig_calibration_done(icap); ++i)
+		msleep(500);
+
+	if (!mig_calibration_done(icap)) {
+		ICAP_ERR(icap,
+			"MIG calibration timeout after bitstream download");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static inline void free_clock_freq_topology(struct icap *icap)
+{
+	vfree(icap->icap_clock_freq_topology);
+	icap->icap_clock_freq_topology = NULL;
+	icap->icap_clock_freq_topology_length = 0;
+}
+
+static int icap_setup_clock_freq_topology(struct icap *icap,
+	const char *buffer, unsigned long length)
+{
+	if (length == 0)
+		return 0;
+
+	free_clock_freq_topology(icap);
+
+	icap->icap_clock_freq_topology = vmalloc(length);
+	if (!icap->icap_clock_freq_topology)
+		return -ENOMEM;
+
+	memcpy(icap->icap_clock_freq_topology, buffer, length);
+	icap->icap_clock_freq_topology_length = length;
+
+	return 0;
+}
+
+static inline void free_clear_bitstream(struct icap *icap)
+{
+	vfree(icap->icap_clear_bitstream);
+	icap->icap_clear_bitstream = NULL;
+	icap->icap_clear_bitstream_length = 0;
+}
+
+static int icap_setup_clear_bitstream(struct icap *icap,
+	const char *buffer, unsigned long length)
+{
+	if (length == 0)
+		return 0;
+
+	free_clear_bitstream(icap);
+
+	icap->icap_clear_bitstream = vmalloc(length);
+	if (!icap->icap_clear_bitstream)
+		return -ENOMEM;
+
+	memcpy(icap->icap_clear_bitstream, buffer, length);
+	icap->icap_clear_bitstream_length = length;
+
+	return 0;
+}
+
+static int wait_for_done(struct icap *icap)
+{
+	u32 w;
+	int i = 0;
+
+	for (i = 0; i < 10; i++) {
+		udelay(5);
+		w = reg_rd(&icap->icap_regs->ir_sr);
+		ICAP_INFO(icap, "XHWICAP_SR: %x", w);
+		if (w & 0x5)
+			return 0;
+	}
+
+	ICAP_ERR(icap, "bitstream download timeout");
+	return -ETIMEDOUT;
+}
+
+static int icap_write(struct icap *icap, const u32 *word_buf, int size)
+{
+	int i;
+	u32 value = 0;
+
+	for (i = 0; i < size; i++) {
+		value = be32_to_cpu(word_buf[i]);
+		reg_wr(&icap->icap_regs->ir_wf, value);
+	}
+
+	reg_wr(&icap->icap_regs->ir_cr, 0x1);
+
+	for (i = 0; i < 20; i++) {
+		value = reg_rd(&icap->icap_regs->ir_cr);
+		if ((value & 0x1) == 0)
+			return 0;
+		ndelay(50);
+	}
+
+	ICAP_ERR(icap, "writing %d dwords timeout", size);
+	return -EIO;
+}
+
+static uint64_t icap_get_section_size(struct icap *icap, enum axlf_section_kind kind)
+{
+	uint64_t size = 0;
+
+	switch (kind) {
+	case IP_LAYOUT:
+		size = sizeof_sect(icap->ip_layout, m_ip_data);
+		break;
+	case MEM_TOPOLOGY:
+		size = sizeof_sect(icap->mem_topo, m_mem_data);
+		break;
+	case DEBUG_IP_LAYOUT:
+		size = sizeof_sect(icap->debug_layout, m_debug_ip_data);
+		break;
+	case CONNECTIVITY:
+		size = sizeof_sect(icap->connectivity, m_connection);
+		break;
+	default:
+		break;
+	}
+
+	return size;
+}
+
+static int bitstream_parse_header(struct icap *icap, const unsigned char *Data,
+	unsigned int Size, struct XHwIcap_Bit_Header *Header)
+{
+	unsigned int I;
+	unsigned int Len;
+	unsigned int Tmp;
+	unsigned int Index;
+
+	/* Start Index at start of bitstream */
+	Index = 0;
+
+	/* Initialize HeaderLength.  If header returned early inidicates
+	 * failure.
+	 */
+	Header->HeaderLength = XHI_BIT_HEADER_FAILURE;
+
+	/* Get "Magic" length */
+	Header->MagicLength = Data[Index++];
+	Header->MagicLength = (Header->MagicLength << 8) | Data[Index++];
+
+	/* Read in "magic" */
+	for (I = 0; I < Header->MagicLength - 1; I++) {
+		Tmp = Data[Index++];
+		if (I%2 == 0 && Tmp != XHI_EVEN_MAGIC_BYTE)
+			return -1;   /* INVALID_FILE_HEADER_ERROR */
+
+		if (I%2 == 1 && Tmp != XHI_ODD_MAGIC_BYTE)
+			return -1;   /* INVALID_FILE_HEADER_ERROR */
+
+	}
+
+	/* Read null end of magic data. */
+	Tmp = Data[Index++];
+
+	/* Read 0x01 (short) */
+	Tmp = Data[Index++];
+	Tmp = (Tmp << 8) | Data[Index++];
+
+	/* Check the "0x01" half word */
+	if (Tmp != 0x01)
+		return -1;	 /* INVALID_FILE_HEADER_ERROR */
+
+	/* Read 'a' */
+	Tmp = Data[Index++];
+	if (Tmp != 'a')
+		return -1;	  /* INVALID_FILE_HEADER_ERROR	*/
+
+	/* Get Design Name length */
+	Len = Data[Index++];
+	Len = (Len << 8) | Data[Index++];
+
+	/* allocate space for design name and final null character. */
+	Header->DesignName = kmalloc(Len, GFP_KERNEL);
+
+	/* Read in Design Name */
+	for (I = 0; I < Len; I++)
+		Header->DesignName[I] = Data[Index++];
+
+
+	if (Header->DesignName[Len-1] != '\0')
+		return -1;
+
+	/* Read 'b' */
+	Tmp = Data[Index++];
+	if (Tmp != 'b')
+		return -1;	/* INVALID_FILE_HEADER_ERROR */
+
+	/* Get Part Name length */
+	Len = Data[Index++];
+	Len = (Len << 8) | Data[Index++];
+
+	/* allocate space for part name and final null character. */
+	Header->PartName = kmalloc(Len, GFP_KERNEL);
+
+	/* Read in part name */
+	for (I = 0; I < Len; I++)
+		Header->PartName[I] = Data[Index++];
+
+	if (Header->PartName[Len-1] != '\0')
+		return -1;
+
+	/* Read 'c' */
+	Tmp = Data[Index++];
+	if (Tmp != 'c')
+		return -1;	/* INVALID_FILE_HEADER_ERROR */
+
+	/* Get date length */
+	Len = Data[Index++];
+	Len = (Len << 8) | Data[Index++];
+
+	/* allocate space for date and final null character. */
+	Header->Date = kmalloc(Len, GFP_KERNEL);
+
+	/* Read in date name */
+	for (I = 0; I < Len; I++)
+		Header->Date[I] = Data[Index++];
+
+	if (Header->Date[Len - 1] != '\0')
+		return -1;
+
+	/* Read 'd' */
+	Tmp = Data[Index++];
+	if (Tmp != 'd')
+		return -1;	/* INVALID_FILE_HEADER_ERROR  */
+
+	/* Get time length */
+	Len = Data[Index++];
+	Len = (Len << 8) | Data[Index++];
+
+	/* allocate space for time and final null character. */
+	Header->Time = kmalloc(Len, GFP_KERNEL);
+
+	/* Read in time name */
+	for (I = 0; I < Len; I++)
+		Header->Time[I] = Data[Index++];
+
+	if (Header->Time[Len - 1] != '\0')
+		return -1;
+
+	/* Read 'e' */
+	Tmp = Data[Index++];
+	if (Tmp != 'e')
+		return -1;	/* INVALID_FILE_HEADER_ERROR */
+
+	/* Get byte length of bitstream */
+	Header->BitstreamLength = Data[Index++];
+	Header->BitstreamLength = (Header->BitstreamLength << 8) | Data[Index++];
+	Header->BitstreamLength = (Header->BitstreamLength << 8) | Data[Index++];
+	Header->BitstreamLength = (Header->BitstreamLength << 8) | Data[Index++];
+	Header->HeaderLength = Index;
+
+	ICAP_INFO(icap, "Design \"%s\"", Header->DesignName);
+	ICAP_INFO(icap, "Part \"%s\"", Header->PartName);
+	ICAP_INFO(icap, "Timestamp \"%s %s\"", Header->Time, Header->Date);
+	ICAP_INFO(icap, "Raw data size 0x%x", Header->BitstreamLength);
+	return 0;
+}
+
+static int bitstream_helper(struct icap *icap, const u32 *word_buffer,
+			    unsigned int word_count)
+{
+	unsigned int remain_word;
+	unsigned int word_written = 0;
+	int wr_fifo_vacancy = 0;
+	int err = 0;
+
+	for (remain_word = word_count; remain_word > 0;
+		remain_word -= word_written, word_buffer += word_written) {
+		wr_fifo_vacancy = reg_rd(&icap->icap_regs->ir_wfv);
+		if (wr_fifo_vacancy <= 0) {
+			ICAP_ERR(icap, "no vacancy: %d", wr_fifo_vacancy);
+			err = -EIO;
+			break;
+		}
+		word_written = (wr_fifo_vacancy < remain_word) ?
+			wr_fifo_vacancy : remain_word;
+		if (icap_write(icap, word_buffer, word_written) != 0) {
+			err = -EIO;
+			break;
+		}
+	}
+
+	return err;
+}
+
+static long icap_download(struct icap *icap, const char *buffer,
+	unsigned long length)
+{
+	long err = 0;
+	struct XHwIcap_Bit_Header bit_header = { 0 };
+	unsigned int numCharsRead = DMA_HWICAP_BITFILE_BUFFER_SIZE;
+	unsigned int byte_read;
+
+	BUG_ON(!buffer);
+	BUG_ON(!length);
+
+	if (bitstream_parse_header(icap, buffer,
+		DMA_HWICAP_BITFILE_BUFFER_SIZE, &bit_header)) {
+		err = -EINVAL;
+		goto free_buffers;
+	}
+
+	if ((bit_header.HeaderLength + bit_header.BitstreamLength) > length) {
+		err = -EINVAL;
+		goto free_buffers;
+	}
+
+	buffer += bit_header.HeaderLength;
+
+	for (byte_read = 0; byte_read < bit_header.BitstreamLength;
+		byte_read += numCharsRead) {
+		numCharsRead = bit_header.BitstreamLength - byte_read;
+		if (numCharsRead > DMA_HWICAP_BITFILE_BUFFER_SIZE)
+			numCharsRead = DMA_HWICAP_BITFILE_BUFFER_SIZE;
+
+		err = bitstream_helper(icap, (u32 *)buffer,
+				       numCharsRead / sizeof(u32));
+		if (err)
+			goto free_buffers;
+		buffer += numCharsRead;
+	}
+
+	err = wait_for_done(icap);
+
+free_buffers:
+	kfree(bit_header.DesignName);
+	kfree(bit_header.PartName);
+	kfree(bit_header.Date);
+	kfree(bit_header.Time);
+	return err;
+}
+
+static const struct axlf_section_header *get_axlf_section_hdr(
+	struct icap *icap, const struct axlf *top, enum axlf_section_kind kind)
+{
+	int i;
+	const struct axlf_section_header *hdr = NULL;
+
+	ICAP_INFO(icap,
+		"trying to find section header for axlf section %d", kind);
+
+	for (i = 0; i < top->m_header.m_numSections; i++) {
+		ICAP_INFO(icap, "saw section header: %d",
+			top->m_sections[i].m_sectionKind);
+		if (top->m_sections[i].m_sectionKind == kind) {
+			hdr = &top->m_sections[i];
+			break;
+		}
+	}
+
+	if (hdr) {
+		if ((hdr->m_sectionOffset + hdr->m_sectionSize) >
+			top->m_header.m_length) {
+			ICAP_INFO(icap, "found section is invalid");
+			hdr = NULL;
+		} else {
+			ICAP_INFO(icap, "header offset: %llu, size: %llu",
+				hdr->m_sectionOffset, hdr->m_sectionSize);
+		}
+	} else {
+		ICAP_INFO(icap, "could not find section header %d", kind);
+	}
+
+	return hdr;
+}
+
+static int alloc_and_get_axlf_section(struct icap *icap,
+	const struct axlf *top, enum axlf_section_kind kind,
+	void **addr, uint64_t *size)
+{
+	void *section = NULL;
+	const struct axlf_section_header *hdr =
+		get_axlf_section_hdr(icap, top, kind);
+
+	if (hdr == NULL)
+		return -EINVAL;
+
+	section = vmalloc(hdr->m_sectionSize);
+	if (section == NULL)
+		return -ENOMEM;
+
+	memcpy(section, ((const char *)top) + hdr->m_sectionOffset,
+		hdr->m_sectionSize);
+
+	*addr = section;
+	*size = hdr->m_sectionSize;
+	return 0;
+}
+
+static int icap_download_boot_firmware(struct platform_device *pdev)
+{
+	struct icap *icap = platform_get_drvdata(pdev);
+	struct pci_dev *pcidev = XOCL_PL_TO_PCI_DEV(pdev);
+	struct pci_dev *pcidev_user = NULL;
+	xdev_handle_t xdev = xocl_get_xdev(pdev);
+	int funcid = PCI_FUNC(pcidev->devfn);
+	int slotid = PCI_SLOT(pcidev->devfn);
+	unsigned short deviceid = pcidev->device;
+	struct axlf *bin_obj_axlf;
+	const struct firmware *fw;
+	char fw_name[128];
+	struct XHwIcap_Bit_Header bit_header = { 0 };
+	long err = 0;
+	uint64_t length = 0;
+	uint64_t primaryFirmwareOffset = 0;
+	uint64_t primaryFirmwareLength = 0;
+	uint64_t secondaryFirmwareOffset = 0;
+	uint64_t secondaryFirmwareLength = 0;
+	uint64_t mbBinaryOffset = 0;
+	uint64_t mbBinaryLength = 0;
+	const struct axlf_section_header *primaryHeader = 0;
+	const struct axlf_section_header *secondaryHeader = 0;
+	const struct axlf_section_header *mbHeader = 0;
+	bool load_mbs = false;
+
+	/* Can only be done from mgmt pf. */
+	if (!ICAP_PRIVILEGED(icap))
+		return -EPERM;
+
+	/* Read dsabin from file system. */
+
+	if (funcid != 0) {
+		pcidev_user = pci_get_slot(pcidev->bus,
+			PCI_DEVFN(slotid, funcid - 1));
+		if (!pcidev_user) {
+			pcidev_user = pci_get_device(pcidev->vendor,
+				pcidev->device + 1, NULL);
+		}
+		if (pcidev_user)
+			deviceid = pcidev_user->device;
+	}
+
+	snprintf(fw_name, sizeof(fw_name),
+		"xilinx/%04x-%04x-%04x-%016llx.dsabin",
+		le16_to_cpu(pcidev->vendor),
+		le16_to_cpu(deviceid),
+		le16_to_cpu(pcidev->subsystem_device),
+		le64_to_cpu(xocl_get_timestamp(xdev)));
+	ICAP_INFO(icap, "try load dsabin %s", fw_name);
+	err = request_firmware(&fw, fw_name, &pcidev->dev);
+	if (err) {
+		snprintf(fw_name, sizeof(fw_name),
+			"xilinx/%04x-%04x-%04x-%016llx.dsabin",
+			le16_to_cpu(pcidev->vendor),
+			le16_to_cpu(deviceid + 1),
+			le16_to_cpu(pcidev->subsystem_device),
+			le64_to_cpu(xocl_get_timestamp(xdev)));
+		ICAP_INFO(icap, "try load dsabin %s", fw_name);
+		err = request_firmware(&fw, fw_name, &pcidev->dev);
+	}
+	/* Retry with the legacy dsabin. */
+	if (err) {
+		snprintf(fw_name, sizeof(fw_name),
+			"xilinx/%04x-%04x-%04x-%016llx.dsabin",
+			le16_to_cpu(pcidev->vendor),
+			le16_to_cpu(pcidev->device + 1),
+			le16_to_cpu(pcidev->subsystem_device),
+			le64_to_cpu(0x0000000000000000));
+		ICAP_INFO(icap, "try load dsabin %s", fw_name);
+		err = request_firmware(&fw, fw_name, &pcidev->dev);
+	}
+	if (err) {
+		/* Give up on finding .dsabin. */
+		ICAP_ERR(icap, "unable to find firmware, giving up");
+		return err;
+	}
+
+	/* Grab lock and touch hardware. */
+	mutex_lock(&icap->icap_lock);
+
+	if (xocl_mb_sched_on(xdev)) {
+		/* Try locating the microblaze binary. */
+		bin_obj_axlf = (struct axlf *)fw->data;
+		mbHeader = get_axlf_section_hdr(icap, bin_obj_axlf, SCHED_FIRMWARE);
+		if (mbHeader) {
+			mbBinaryOffset = mbHeader->m_sectionOffset;
+			mbBinaryLength = mbHeader->m_sectionSize;
+			length = bin_obj_axlf->m_header.m_length;
+			xocl_mb_load_sche_image(xdev, fw->data + mbBinaryOffset,
+				mbBinaryLength);
+			ICAP_INFO(icap, "stashed mb sche binary");
+			load_mbs = true;
+		}
+	}
+
+	if (xocl_mb_mgmt_on(xdev)) {
+		/* Try locating the board mgmt binary. */
+		bin_obj_axlf = (struct axlf *)fw->data;
+		mbHeader = get_axlf_section_hdr(icap, bin_obj_axlf, FIRMWARE);
+		if (mbHeader) {
+			mbBinaryOffset = mbHeader->m_sectionOffset;
+			mbBinaryLength = mbHeader->m_sectionSize;
+			length = bin_obj_axlf->m_header.m_length;
+			xocl_mb_load_mgmt_image(xdev, fw->data + mbBinaryOffset,
+				mbBinaryLength);
+			ICAP_INFO(icap, "stashed mb mgmt binary");
+			load_mbs = true;
+		}
+	}
+
+	if (load_mbs)
+		xocl_mb_reset(xdev);
+
+
+	if (memcmp(fw->data, ICAP_XCLBIN_V2, sizeof(ICAP_XCLBIN_V2)) != 0) {
+		ICAP_ERR(icap, "invalid firmware %s", fw_name);
+		err = -EINVAL;
+		goto done;
+	}
+
+	ICAP_INFO(icap, "boot_firmware in axlf format");
+	bin_obj_axlf = (struct axlf *)fw->data;
+	length = bin_obj_axlf->m_header.m_length;
+	/* Match the xclbin with the hardware. */
+	if (!xocl_verify_timestamp(xdev,
+		bin_obj_axlf->m_header.m_featureRomTimeStamp)) {
+		ICAP_ERR(icap, "timestamp of ROM did not match xclbin");
+		err = -EINVAL;
+		goto done;
+	}
+	ICAP_INFO(icap, "VBNV and timestamps matched");
+
+	if (xocl_xrt_version_check(xdev, bin_obj_axlf, true)) {
+		ICAP_ERR(icap, "Major version does not match xrt");
+		err = -EINVAL;
+		goto done;
+	}
+	ICAP_INFO(icap, "runtime version matched");
+
+	primaryHeader = get_axlf_section_hdr(icap, bin_obj_axlf, BITSTREAM);
+	secondaryHeader = get_axlf_section_hdr(icap, bin_obj_axlf,
+		CLEARING_BITSTREAM);
+	if (primaryHeader) {
+		primaryFirmwareOffset = primaryHeader->m_sectionOffset;
+		primaryFirmwareLength = primaryHeader->m_sectionSize;
+	}
+	if (secondaryHeader) {
+		secondaryFirmwareOffset = secondaryHeader->m_sectionOffset;
+		secondaryFirmwareLength = secondaryHeader->m_sectionSize;
+	}
+
+	if (length > fw->size) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	if ((primaryFirmwareOffset + primaryFirmwareLength) > length) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	if ((secondaryFirmwareOffset + secondaryFirmwareLength) > length) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	if (primaryFirmwareLength) {
+		ICAP_INFO(icap,
+			"found second stage bitstream of size 0x%llx in %s",
+			primaryFirmwareLength, fw_name);
+		err = icap_download(icap, fw->data + primaryFirmwareOffset,
+			primaryFirmwareLength);
+		/*
+		 * If we loaded a new second stage, we do not need the
+		 * previously stashed clearing bitstream if any.
+		 */
+		free_clear_bitstream(icap);
+		if (err) {
+			ICAP_ERR(icap,
+				"failed to download second stage bitstream");
+			goto done;
+		}
+		ICAP_INFO(icap, "downloaded second stage bitstream");
+	}
+
+	/*
+	 * If both primary and secondary bitstreams have been provided then
+	 * ignore the previously stashed bitstream if any. If only secondary
+	 * bitstream was provided, but we found a previously stashed bitstream
+	 * we should use the latter since it is more appropriate for the
+	 * current state of the device
+	 */
+	if (secondaryFirmwareLength && (primaryFirmwareLength ||
+		!icap->icap_clear_bitstream)) {
+		free_clear_bitstream(icap);
+		icap->icap_clear_bitstream = vmalloc(secondaryFirmwareLength);
+		if (!icap->icap_clear_bitstream) {
+			err = -ENOMEM;
+			goto done;
+		}
+		icap->icap_clear_bitstream_length = secondaryFirmwareLength;
+		memcpy(icap->icap_clear_bitstream,
+			fw->data + secondaryFirmwareOffset,
+			icap->icap_clear_bitstream_length);
+		ICAP_INFO(icap, "found clearing bitstream of size 0x%lx in %s",
+			icap->icap_clear_bitstream_length, fw_name);
+	} else if (icap->icap_clear_bitstream) {
+		ICAP_INFO(icap,
+			"using existing clearing bitstream of size 0x%lx",
+		       icap->icap_clear_bitstream_length);
+	}
+
+	if (icap->icap_clear_bitstream &&
+		bitstream_parse_header(icap, icap->icap_clear_bitstream,
+		DMA_HWICAP_BITFILE_BUFFER_SIZE, &bit_header)) {
+		err = -EINVAL;
+		free_clear_bitstream(icap);
+	}
+
+done:
+	mutex_unlock(&icap->icap_lock);
+	release_firmware(fw);
+	kfree(bit_header.DesignName);
+	kfree(bit_header.PartName);
+	kfree(bit_header.Date);
+	kfree(bit_header.Time);
+	ICAP_INFO(icap, "%s err: %ld", __func__, err);
+	return err;
+}
+
+
+static long icap_download_clear_bitstream(struct icap *icap)
+{
+	long err = 0;
+	const char *buffer = icap->icap_clear_bitstream;
+	unsigned long length = icap->icap_clear_bitstream_length;
+
+	ICAP_INFO(icap, "downloading clear bitstream of length 0x%lx", length);
+
+	if (!buffer)
+		return 0;
+
+	err = icap_download(icap, buffer, length);
+
+	free_clear_bitstream(icap);
+	return err;
+}
+
+/*
+ * This function should be called with icap_mutex lock held
+ */
+static long axlf_set_freqscaling(struct icap *icap, struct platform_device *pdev,
+	const char *clk_buf, unsigned long length)
+{
+	struct clock_freq_topology *freqs = NULL;
+	int clock_type_count = 0;
+	int i = 0;
+	struct clock_freq *freq = NULL;
+	int data_clk_count = 0;
+	int kernel_clk_count = 0;
+	int system_clk_count = 0;
+	unsigned short target_freqs[4] = {0};
+
+	freqs = (struct clock_freq_topology *)clk_buf;
+	if (freqs->m_count > 4) {
+		ICAP_ERR(icap, "More than 4 clocks found in clock topology");
+		return -EDOM;
+	}
+
+	//Error checks - we support 1 data clk (reqd), one kernel clock(reqd) and
+	//at most 2 system clocks (optional/reqd for aws).
+	//Data clk needs to be the first entry, followed by kernel clock
+	//and then system clocks
+	//
+
+	for (i = 0; i < freqs->m_count; i++) {
+		freq = &(freqs->m_clock_freq[i]);
+		if (freq->m_type == CT_DATA)
+			data_clk_count++;
+
+		if (freq->m_type == CT_KERNEL)
+			kernel_clk_count++;
+
+		if (freq->m_type == CT_SYSTEM)
+			system_clk_count++;
+
+	}
+
+	if (data_clk_count != 1) {
+		ICAP_ERR(icap, "Data clock not found in clock topology");
+		return -EDOM;
+	}
+	if (kernel_clk_count != 1) {
+		ICAP_ERR(icap, "Kernel clock not found in clock topology");
+		return -EDOM;
+	}
+	if (system_clk_count > 2) {
+		ICAP_ERR(icap,
+			"More than 2 system clocks found in clock topology");
+		return -EDOM;
+	}
+
+	for (i = 0; i < freqs->m_count; i++) {
+		freq = &(freqs->m_clock_freq[i]);
+		if (freq->m_type == CT_DATA)
+			target_freqs[0] = freq->m_freq_Mhz;
+	}
+
+	for (i = 0; i < freqs->m_count; i++) {
+		freq = &(freqs->m_clock_freq[i]);
+		if (freq->m_type == CT_KERNEL)
+			target_freqs[1] = freq->m_freq_Mhz;
+	}
+
+	clock_type_count = 2;
+	for (i = 0; i < freqs->m_count; i++) {
+		freq = &(freqs->m_clock_freq[i]);
+		if (freq->m_type == CT_SYSTEM)
+			target_freqs[clock_type_count++] = freq->m_freq_Mhz;
+	}
+
+
+	ICAP_INFO(icap, "setting clock freq, "
+		"num: %lu, data_freq: %d , clk_freq: %d, "
+		"sys_freq[0]: %d, sys_freq[1]: %d",
+		ARRAY_SIZE(target_freqs), target_freqs[0], target_freqs[1],
+		target_freqs[2], target_freqs[3]);
+	return set_freqs(icap, target_freqs, 4);
+}
+
+
+static int icap_download_user(struct icap *icap, const char *bit_buf,
+	unsigned long length)
+{
+	long err = 0;
+	struct XHwIcap_Bit_Header bit_header = { 0 };
+	unsigned int numCharsRead = DMA_HWICAP_BITFILE_BUFFER_SIZE;
+	unsigned int byte_read;
+
+	ICAP_INFO(icap, "downloading bitstream, length: %lu", length);
+
+	icap_freeze_axi_gate(icap);
+
+	err = icap_download_clear_bitstream(icap);
+	if (err)
+		goto free_buffers;
+
+	if (bitstream_parse_header(icap, bit_buf,
+		DMA_HWICAP_BITFILE_BUFFER_SIZE, &bit_header)) {
+		err = -EINVAL;
+		goto free_buffers;
+	}
+	if ((bit_header.HeaderLength + bit_header.BitstreamLength) > length) {
+		err = -EINVAL;
+		goto free_buffers;
+	}
+
+	bit_buf += bit_header.HeaderLength;
+	for (byte_read = 0; byte_read < bit_header.BitstreamLength;
+		byte_read += numCharsRead) {
+		numCharsRead = bit_header.BitstreamLength - byte_read;
+		if (numCharsRead > DMA_HWICAP_BITFILE_BUFFER_SIZE)
+			numCharsRead = DMA_HWICAP_BITFILE_BUFFER_SIZE;
+
+		err = bitstream_helper(icap, (u32 *)bit_buf,
+				       numCharsRead / sizeof(u32));
+		if (err)
+			goto free_buffers;
+
+		bit_buf += numCharsRead;
+	}
+
+	err = wait_for_done(icap);
+	if (err)
+		goto free_buffers;
+
+	/*
+	 * Perform frequency scaling since PR download can silenty overwrite
+	 * MMCM settings in static region changing the clock frequencies
+	 * although ClockWiz CONFIG registers will misleading report the older
+	 * configuration from before bitstream download as if nothing has
+	 * changed.
+	 */
+	if (!err)
+		err = icap_ocl_freqscaling(icap, true);
+
+free_buffers:
+	icap_free_axi_gate(icap);
+	kfree(bit_header.DesignName);
+	kfree(bit_header.PartName);
+	kfree(bit_header.Date);
+	kfree(bit_header.Time);
+	return err;
+}
+
+
+static int __icap_lock_peer(struct platform_device *pdev, const uuid_t *id)
+{
+	int err = 0;
+	struct icap *icap = platform_get_drvdata(pdev);
+	int resp = 0;
+	size_t resplen = sizeof(resp);
+	struct mailbox_req_bitstream_lock bitstream_lock = {0};
+	size_t data_len = sizeof(struct mailbox_req_bitstream_lock);
+	struct mailbox_req *mb_req = NULL;
+	size_t reqlen = sizeof(struct mailbox_req) + data_len;
+	/* if there is no user there
+	 * ask mgmt to lock the bitstream
+	 */
+	if (icap->icap_bitstream_ref == 0) {
+		mb_req = vmalloc(reqlen);
+		if (!mb_req) {
+			err = -ENOMEM;
+			goto done;
+		}
+
+		mb_req->req = MAILBOX_REQ_LOCK_BITSTREAM;
+		uuid_copy(&bitstream_lock.uuid, id);
+
+		memcpy(mb_req->data, &bitstream_lock, data_len);
+
+		err = xocl_peer_request(XOCL_PL_DEV_TO_XDEV(pdev),
+			mb_req, reqlen, &resp, &resplen, NULL, NULL);
+
+		if (err) {
+			err = -ENODEV;
+			goto done;
+		}
+
+		if (resp < 0) {
+			err = resp;
+			goto done;
+		}
+	}
+
+done:
+	vfree(mb_req);
+	return err;
+}
+
+static int __icap_unlock_peer(struct platform_device *pdev, const uuid_t *id)
+{
+	int err = 0;
+	struct icap *icap = platform_get_drvdata(pdev);
+	struct mailbox_req_bitstream_lock bitstream_lock = {0};
+	size_t data_len = sizeof(struct mailbox_req_bitstream_lock);
+	struct mailbox_req *mb_req = NULL;
+	size_t reqlen = sizeof(struct mailbox_req) + data_len;
+	/* if there is no user there
+	 * ask mgmt to unlock the bitstream
+	 */
+	if (icap->icap_bitstream_ref == 0) {
+		mb_req = vmalloc(reqlen);
+		if (!mb_req) {
+			err = -ENOMEM;
+			goto done;
+		}
+
+		mb_req->req = MAILBOX_REQ_UNLOCK_BITSTREAM;
+		memcpy(mb_req->data, &bitstream_lock, data_len);
+
+		err = xocl_peer_notify(XOCL_PL_DEV_TO_XDEV(pdev), mb_req, reqlen);
+		if (err) {
+			err = -ENODEV;
+			goto done;
+		}
+	}
+done:
+	vfree(mb_req);
+	return err;
+}
+
+
+static int icap_download_bitstream_axlf(struct platform_device *pdev,
+	const void *u_xclbin)
+{
+	/*
+	 * decouple as 1. download xclbin, 2. parse xclbin 3. verify xclbin
+	 */
+	struct icap *icap = platform_get_drvdata(pdev);
+	long err = 0;
+	uint64_t primaryFirmwareOffset = 0;
+	uint64_t primaryFirmwareLength = 0;
+	uint64_t secondaryFirmwareOffset = 0;
+	uint64_t secondaryFirmwareLength = 0;
+	const struct axlf_section_header *primaryHeader = NULL;
+	const struct axlf_section_header *clockHeader = NULL;
+	const struct axlf_section_header *secondaryHeader = NULL;
+	struct axlf *xclbin = (struct axlf *)u_xclbin;
+	char *buffer;
+	xdev_handle_t xdev = xocl_get_xdev(pdev);
+	bool need_download;
+	int msg = -ETIMEDOUT;
+	size_t resplen = sizeof(msg);
+	int pid = pid_nr(task_tgid(current));
+	uint32_t data_len = 0;
+	int peer_connected;
+	struct mailbox_req *mb_req = NULL;
+	struct mailbox_bitstream_kaddr mb_addr = {0};
+	uuid_t peer_uuid;
+
+	if (memcmp(xclbin->m_magic, ICAP_XCLBIN_V2, sizeof(ICAP_XCLBIN_V2)))
+		return -EINVAL;
+
+	if (ICAP_PRIVILEGED(icap)) {
+		if (xocl_xrt_version_check(xdev, xclbin, true)) {
+			ICAP_ERR(icap, "XRT version does not match");
+			return -EINVAL;
+		}
+
+		/* Match the xclbin with the hardware. */
+		if (!xocl_verify_timestamp(xdev,
+			xclbin->m_header.m_featureRomTimeStamp)) {
+			ICAP_ERR(icap, "timestamp of ROM not match Xclbin");
+			xocl_sysfs_error(xdev, "timestamp of ROM not match Xclbin");
+			return -EINVAL;
+		}
+
+		mutex_lock(&icap->icap_lock);
+
+		ICAP_INFO(icap,
+			"incoming xclbin: %016llx, on device xclbin: %016llx",
+			xclbin->m_uniqueId, icap->icap_bitstream_id);
+
+		need_download = (icap->icap_bitstream_id != xclbin->m_uniqueId);
+
+		if (!need_download) {
+			/*
+			 * No need to download, if xclbin exists already.
+			 * But, still need to reset CUs.
+			 */
+			if (!icap_bitstream_in_use(icap, 0)) {
+				icap_freeze_axi_gate(icap);
+				msleep(50);
+				icap_free_axi_gate(icap);
+				msleep(50);
+			}
+			ICAP_INFO(icap, "bitstream already exists, skip downloading");
+		}
+
+		mutex_unlock(&icap->icap_lock);
+
+		if (!need_download)
+			return 0;
+
+		/*
+		 * Find sections in xclbin.
+		 */
+		ICAP_INFO(icap, "finding CLOCK_FREQ_TOPOLOGY section");
+		/* Read the CLOCK section but defer changing clocks to later */
+		clockHeader = get_axlf_section_hdr(icap, xclbin,
+			CLOCK_FREQ_TOPOLOGY);
+
+		ICAP_INFO(icap, "finding bitstream sections");
+		primaryHeader = get_axlf_section_hdr(icap, xclbin, BITSTREAM);
+		if (primaryHeader == NULL) {
+			err = -EINVAL;
+			goto done;
+		}
+		primaryFirmwareOffset = primaryHeader->m_sectionOffset;
+		primaryFirmwareLength = primaryHeader->m_sectionSize;
+
+		secondaryHeader = get_axlf_section_hdr(icap, xclbin,
+			CLEARING_BITSTREAM);
+		if (secondaryHeader) {
+			if (XOCL_PL_TO_PCI_DEV(pdev)->device == 0x7138) {
+				err = -EINVAL;
+				goto done;
+			} else {
+				secondaryFirmwareOffset =
+					secondaryHeader->m_sectionOffset;
+				secondaryFirmwareLength =
+					secondaryHeader->m_sectionSize;
+			}
+		}
+
+		mutex_lock(&icap->icap_lock);
+
+		if (icap_bitstream_in_use(icap, 0)) {
+			ICAP_ERR(icap, "bitstream is locked, can't download new one");
+			err = -EBUSY;
+			goto done;
+		}
+
+		/* All clear, go ahead and start fiddling with hardware */
+
+		if (clockHeader != NULL) {
+			uint64_t clockFirmwareOffset = clockHeader->m_sectionOffset;
+			uint64_t clockFirmwareLength = clockHeader->m_sectionSize;
+
+			buffer = (char *)xclbin;
+			buffer += clockFirmwareOffset;
+			err = axlf_set_freqscaling(icap, pdev, buffer, clockFirmwareLength);
+			if (err)
+				goto done;
+			err = icap_setup_clock_freq_topology(icap, buffer, clockFirmwareLength);
+			if (err)
+				goto done;
+		}
+
+		icap->icap_bitstream_id = 0;
+		uuid_copy(&icap->icap_bitstream_uuid, &uuid_null);
+
+		buffer = (char *)xclbin;
+		buffer += primaryFirmwareOffset;
+		err = icap_download_user(icap, buffer, primaryFirmwareLength);
+		if (err)
+			goto done;
+
+		buffer = (char *)u_xclbin;
+		buffer += secondaryFirmwareOffset;
+		err = icap_setup_clear_bitstream(icap, buffer, secondaryFirmwareLength);
+		if (err)
+			goto done;
+
+		if ((xocl_is_unified(xdev) || XOCL_DSA_XPR_ON(xdev)))
+			err = calibrate_mig(icap);
+		if (err)
+			goto done;
+
+		/* Remember "this" bitstream, so avoid redownload the next time. */
+		icap->icap_bitstream_id = xclbin->m_uniqueId;
+		if (!uuid_is_null(&xclbin->m_header.uuid)) {
+			uuid_copy(&icap->icap_bitstream_uuid, &xclbin->m_header.uuid);
+		} else {
+			// Legacy xclbin, convert legacy id to new id
+			memcpy(&icap->icap_bitstream_uuid,
+				&xclbin->m_header.m_timeStamp, 8);
+		}
+	} else {
+
+		mutex_lock(&icap->icap_lock);
+
+		if (icap_bitstream_in_use(icap, pid)) {
+			if (!uuid_equal(&xclbin->m_header.uuid, &icap->icap_bitstream_uuid)) {
+				err = -EBUSY;
+				goto done;
+			}
+		}
+
+		icap_read_from_peer(pdev, XCLBIN_UUID, &peer_uuid, sizeof(uuid_t));
+
+		if (!uuid_equal(&peer_uuid, &xclbin->m_header.uuid)) {
+			/*
+			 *  should replace with userpf download flow
+			 */
+			peer_connected = xocl_mailbox_get_data(xdev, PEER_CONN);
+			ICAP_INFO(icap, "%s peer_connected 0x%x", __func__,
+				peer_connected);
+			if (peer_connected < 0) {
+				err = -ENODEV;
+				goto done;
+			}
+
+			if (!(peer_connected & MB_PEER_CONNECTED)) {
+				ICAP_ERR(icap, "%s fail to find peer, abort!",
+					__func__);
+				err = -EFAULT;
+				goto done;
+			}
+
+			if ((peer_connected & 0xF) == MB_PEER_SAMEDOM_CONNECTED) {
+				data_len = sizeof(struct mailbox_req) + sizeof(struct mailbox_bitstream_kaddr);
+				mb_req = vmalloc(data_len);
+				if (!mb_req) {
+					ICAP_ERR(icap, "Unable to create mb_req\n");
+					err = -ENOMEM;
+					goto done;
+				}
+				mb_req->req = MAILBOX_REQ_LOAD_XCLBIN_KADDR;
+				mb_addr.addr = (uint64_t)xclbin;
+				memcpy(mb_req->data, &mb_addr, sizeof(struct mailbox_bitstream_kaddr));
+
+			} else if ((peer_connected & 0xF) == MB_PEER_CONNECTED) {
+				data_len = sizeof(struct mailbox_req) +
+					xclbin->m_header.m_length;
+				mb_req = vmalloc(data_len);
+				if (!mb_req) {
+					ICAP_ERR(icap, "Unable to create mb_req\n");
+					err = -ENOMEM;
+					goto done;
+				}
+				memcpy(mb_req->data, u_xclbin, xclbin->m_header.m_length);
+				mb_req->req = MAILBOX_REQ_LOAD_XCLBIN;
+			}
+
+			mb_req->data_total_len = data_len;
+			(void) xocl_peer_request(xdev,
+				mb_req, data_len, &msg, &resplen, NULL, NULL);
+
+			if (msg != 0) {
+				ICAP_ERR(icap,
+					"%s peer failed to download xclbin",
+					__func__);
+				err = -EFAULT;
+				goto done;
+			}
+		} else
+			ICAP_INFO(icap, "Already downloaded xclbin ID: %016llx",
+				xclbin->m_uniqueId);
+
+		icap->icap_bitstream_id = xclbin->m_uniqueId;
+		if (!uuid_is_null(&xclbin->m_header.uuid)) {
+			uuid_copy(&icap->icap_bitstream_uuid, &xclbin->m_header.uuid);
+		} else {
+			// Legacy xclbin, convert legacy id to new id
+			memcpy(&icap->icap_bitstream_uuid,
+				&xclbin->m_header.m_timeStamp, 8);
+		}
+
+	}
+
+	if (ICAP_PRIVILEGED(icap)) {
+		icap_parse_bitstream_axlf_section(pdev, xclbin, MEM_TOPOLOGY);
+		icap_parse_bitstream_axlf_section(pdev, xclbin, IP_LAYOUT);
+	} else {
+		icap_parse_bitstream_axlf_section(pdev, xclbin, IP_LAYOUT);
+		icap_parse_bitstream_axlf_section(pdev, xclbin, MEM_TOPOLOGY);
+		icap_parse_bitstream_axlf_section(pdev, xclbin, CONNECTIVITY);
+		icap_parse_bitstream_axlf_section(pdev, xclbin, DEBUG_IP_LAYOUT);
+	}
+
+	if (ICAP_PRIVILEGED(icap))
+		err = icap_verify_bitstream_axlf(pdev, xclbin);
+
+done:
+	mutex_unlock(&icap->icap_lock);
+	vfree(mb_req);
+	ICAP_INFO(icap, "%s err: %ld", __func__, err);
+	return err;
+}
+
+static int icap_verify_bitstream_axlf(struct platform_device *pdev,
+	struct axlf *xclbin)
+{
+	struct icap *icap = platform_get_drvdata(pdev);
+	int err = 0, i;
+	xdev_handle_t xdev = xocl_get_xdev(pdev);
+	bool dna_check = false;
+	uint64_t section_size = 0;
+
+	/* Destroy all dynamically add sub-devices*/
+	xocl_subdev_destroy_by_id(xdev, XOCL_SUBDEV_DNA);
+	xocl_subdev_destroy_by_id(xdev, XOCL_SUBDEV_MIG);
+
+	/*
+	 * Add sub device dynamically.
+	 * restrict any dynamically added sub-device and 1 base address,
+	 * Has pre-defined length
+	 *  Ex:    "ip_data": {
+	 *         "m_type": "IP_DNASC",
+	 *         "properties": "0x0",
+	 *         "m_base_address": "0x1100000", <--  base address
+	 *         "m_name": "slr0\/dna_self_check_0"
+	 */
+
+	if (!icap->ip_layout) {
+		err = -EFAULT;
+		goto done;
+	}
+	for (i = 0; i < icap->ip_layout->m_count; ++i) {
+		struct xocl_subdev_info subdev_info = { 0 };
+		struct resource res = { 0 };
+		struct ip_data *ip = &icap->ip_layout->m_ip_data[i];
+
+		if (ip->m_type == IP_KERNEL)
+			continue;
+
+		if (ip->m_type == IP_DDR4_CONTROLLER) {
+			uint32_t memidx = ip->properties;
+
+			if (!icap->mem_topo || ip->properties >= icap->mem_topo->m_count ||
+				icap->mem_topo->m_mem_data[memidx].m_type !=
+				MEM_DDR4) {
+				ICAP_ERR(icap, "bad ECC controller index: %u",
+					ip->properties);
+				continue;
+			}
+			if (!icap->mem_topo->m_mem_data[memidx].m_used) {
+				ICAP_INFO(icap,
+					"ignore ECC controller for: %s",
+					icap->mem_topo->m_mem_data[memidx].m_tag);
+				continue;
+			}
+			err = xocl_subdev_get_devinfo(XOCL_SUBDEV_MIG,
+				&subdev_info, &res);
+			if (err) {
+				ICAP_ERR(icap, "can't get MIG subdev info");
+				goto done;
+			}
+			res.start += ip->m_base_address;
+			res.end += ip->m_base_address;
+			subdev_info.priv_data =
+				icap->mem_topo->m_mem_data[memidx].m_tag;
+			subdev_info.data_len =
+				sizeof(icap->mem_topo->m_mem_data[memidx].m_tag);
+			err = xocl_subdev_create_multi_inst(xdev, &subdev_info);
+			if (err) {
+				ICAP_ERR(icap, "can't create MIG subdev");
+				goto done;
+			}
+		}
+		if (ip->m_type == IP_DNASC) {
+			dna_check = true;
+			err = xocl_subdev_get_devinfo(XOCL_SUBDEV_DNA,
+				&subdev_info, &res);
+			if (err) {
+				ICAP_ERR(icap, "can't get DNA subdev info");
+				goto done;
+			}
+			res.start += ip->m_base_address;
+			res.end += ip->m_base_address;
+			err = xocl_subdev_create_one(xdev, &subdev_info);
+			if (err) {
+				ICAP_ERR(icap, "can't create DNA subdev");
+				goto done;
+			}
+		}
+	}
+
+	if (dna_check) {
+		bool is_axi = ((xocl_dna_capability(xdev) & 0x1) != 0);
+
+		/*
+		 * Any error occurs here should return -EACCES for app to
+		 * know that DNA has failed.
+		 */
+		err = -EACCES;
+
+		ICAP_INFO(icap, "DNA version: %s", is_axi ? "AXI" : "BRAM");
+
+		if (is_axi) {
+			uint32_t *cert = NULL;
+
+			if (alloc_and_get_axlf_section(icap, xclbin,
+				DNA_CERTIFICATE,
+				(void **)&cert, &section_size) != 0) {
+
+				// We keep dna sub device if IP_DNASC presents
+				ICAP_ERR(icap, "Can't get certificate section");
+				goto dna_cert_fail;
+			}
+
+			ICAP_INFO(icap, "DNA Certificate Size 0x%llx", section_size);
+			if (section_size % 64 || section_size < 576)
+				ICAP_ERR(icap, "Invalid certificate size");
+			else
+				xocl_dna_write_cert(xdev, cert, section_size);
+			vfree(cert);
+		}
+
+		/* Check DNA validation result. */
+		if (0x1 & xocl_dna_status(xdev)) {
+			err = 0; /* xclbin is valid */
+		} else {
+			ICAP_ERR(icap, "DNA inside xclbin is invalid");
+			goto dna_cert_fail;
+		}
+	}
+
+done:
+	if (err) {
+		vfree(icap->connectivity);
+		icap->connectivity = NULL;
+		vfree(icap->ip_layout);
+		icap->ip_layout = NULL;
+		vfree(icap->mem_topo);
+		icap->mem_topo = NULL;
+		xocl_subdev_destroy_by_id(xdev, XOCL_SUBDEV_DNA);
+		xocl_subdev_destroy_by_id(xdev, XOCL_SUBDEV_MIG);
+	}
+dna_cert_fail:
+	return err;
+}
+
+/*
+ * On x86_64, reset hwicap by loading special bitstream sequence which
+ * forces the FPGA to reload from PROM.
+ */
+static int icap_reset_bitstream(struct platform_device *pdev)
+{
+/*
+ * Booting FPGA from PROM
+ * http://www.xilinx.com/support/documentation/user_guides/ug470_7Series_Config.pdf
+ * Table 7.1
+ */
+#define DUMMY_WORD         0xFFFFFFFF
+#define SYNC_WORD          0xAA995566
+#define TYPE1_NOOP         0x20000000
+#define TYPE1_WRITE_WBSTAR 0x30020001
+#define WBSTAR_ADD10       0x00000000
+#define WBSTAR_ADD11       0x01000000
+#define TYPE1_WRITE_CMD    0x30008001
+#define IPROG_CMD          0x0000000F
+#define SWAP_ENDIAN_32(x)						\
+	(unsigned int)((((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | \
+		       (((x) & 0x0000FF00) << 8)  | (((x) & 0x000000FF) << 24))
+	/*
+	 * The bitstream is expected in big endian format
+	 */
+	const unsigned int fpga_boot_seq[] = {SWAP_ENDIAN_32(DUMMY_WORD),
+			SWAP_ENDIAN_32(SYNC_WORD),
+			SWAP_ENDIAN_32(TYPE1_NOOP),
+			SWAP_ENDIAN_32(TYPE1_WRITE_CMD),
+			SWAP_ENDIAN_32(IPROG_CMD),
+			SWAP_ENDIAN_32(TYPE1_NOOP),
+			SWAP_ENDIAN_32(TYPE1_NOOP)};
+
+	struct icap *icap = platform_get_drvdata(pdev);
+	int i;
+
+	/* Can only be done from mgmt pf. */
+	if (!ICAP_PRIVILEGED(icap))
+		return -EPERM;
+
+	mutex_lock(&icap->icap_lock);
+
+	if (icap_bitstream_in_use(icap, 0)) {
+		mutex_unlock(&icap->icap_lock);
+		ICAP_ERR(icap, "bitstream is locked, can't reset");
+		return -EBUSY;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(fpga_boot_seq); i++) {
+		unsigned int value = be32_to_cpu(fpga_boot_seq[i]);
+
+		reg_wr(&icap->icap_regs->ir_wfv, value);
+	}
+	reg_wr(&icap->icap_regs->ir_cr, 0x1);
+
+	msleep(4000);
+
+	mutex_unlock(&icap->icap_lock);
+
+	ICAP_INFO(icap, "reset bitstream is done");
+	return 0;
+}
+
+static int icap_lock_bitstream(struct platform_device *pdev, const uuid_t *id,
+	pid_t pid)
+{
+	struct icap *icap = platform_get_drvdata(pdev);
+	int err = 0;
+
+	if (uuid_is_null(id)) {
+		ICAP_ERR(icap, "proc %d invalid UUID", pid);
+		return -EINVAL;
+	}
+
+	mutex_lock(&icap->icap_lock);
+
+	if (!ICAP_PRIVILEGED(icap)) {
+		err = __icap_lock_peer(pdev, id);
+		if (err < 0)
+			goto done;
+	}
+
+	if (uuid_equal(id, &icap->icap_bitstream_uuid))
+		err = add_user(icap, pid);
+	else
+		err = -EBUSY;
+
+	if (err >= 0)
+		err = icap->icap_bitstream_ref;
+
+	ICAP_INFO(icap, "proc %d try to lock bitstream %pUb, ref=%d, err=%d",
+		  pid, id, icap->icap_bitstream_ref, err);
+done:
+	mutex_unlock(&icap->icap_lock);
+
+	if (!ICAP_PRIVILEGED(icap) && err == 1) /* reset on first reference */
+		xocl_exec_reset(xocl_get_xdev(pdev));
+
+	if (err >= 0)
+		err = 0;
+
+	return err;
+}
+
+static int icap_unlock_bitstream(struct platform_device *pdev, const uuid_t *id,
+	pid_t pid)
+{
+	struct icap *icap = platform_get_drvdata(pdev);
+	int err = 0;
+
+	if (id == NULL)
+		id = &uuid_null;
+
+	mutex_lock(&icap->icap_lock);
+
+	/* Force unlock. */
+	if (uuid_is_null(id))
+		del_all_users(icap);
+	else if (uuid_equal(id, &icap->icap_bitstream_uuid))
+		err = del_user(icap, pid);
+	else
+		err = -EINVAL;
+
+	if (!ICAP_PRIVILEGED(icap))
+		__icap_unlock_peer(pdev, id);
+
+	if (err >= 0)
+		err = icap->icap_bitstream_ref;
+
+	if (!ICAP_PRIVILEGED(icap)) {
+		if (err == 0)
+			xocl_exec_stop(xocl_get_xdev(pdev));
+	}
+
+	ICAP_INFO(icap, "proc %d try to unlock bitstream %pUb, ref=%d, err=%d",
+		  pid, id, icap->icap_bitstream_ref, err);
+
+	mutex_unlock(&icap->icap_lock);
+	if (err >= 0)
+		err = 0;
+	return err;
+}
+
+static int icap_parse_bitstream_axlf_section(struct platform_device *pdev,
+	const struct axlf *xclbin, enum axlf_section_kind kind)
+{
+	struct icap *icap = platform_get_drvdata(pdev);
+	long err = 0;
+	uint64_t section_size = 0, sect_sz = 0;
+	void **target = NULL;
+
+	if (memcmp(xclbin->m_magic, ICAP_XCLBIN_V2, sizeof(ICAP_XCLBIN_V2)))
+		return -EINVAL;
+
+	switch (kind) {
+	case IP_LAYOUT:
+		target = (void **)&icap->ip_layout;
+		break;
+	case MEM_TOPOLOGY:
+		target = (void **)&icap->mem_topo;
+		break;
+	case DEBUG_IP_LAYOUT:
+		target = (void **)&icap->debug_layout;
+		break;
+	case CONNECTIVITY:
+		target = (void **)&icap->connectivity;
+		break;
+	default:
+		break;
+	}
+	if (target) {
+		vfree(*target);
+		*target = NULL;
+	}
+	err = alloc_and_get_axlf_section(icap, xclbin, kind,
+		target, &section_size);
+	if (err != 0)
+		goto done;
+	sect_sz = icap_get_section_size(icap, kind);
+	if (sect_sz > section_size) {
+		err = -EINVAL;
+		goto done;
+	}
+done:
+	if (err) {
+		vfree(*target);
+		*target = NULL;
+	}
+	ICAP_INFO(icap, "%s kind %d, err: %ld", __func__, kind, err);
+	return err;
+}
+
+static uint64_t icap_get_data(struct platform_device *pdev,
+	enum data_kind kind)
+{
+
+	struct icap *icap = platform_get_drvdata(pdev);
+	uint64_t target = 0;
+
+	mutex_lock(&icap->icap_lock);
+	switch (kind) {
+	case IPLAYOUT_AXLF:
+		target = (uint64_t)icap->ip_layout;
+		break;
+	case MEMTOPO_AXLF:
+		target = (uint64_t)icap->mem_topo;
+		break;
+	case DEBUG_IPLAYOUT_AXLF:
+		target = (uint64_t)icap->debug_layout;
+		break;
+	case CONNECTIVITY_AXLF:
+		target = (uint64_t)icap->connectivity;
+		break;
+	case IDCODE:
+		target = icap->idcode;
+		break;
+	case XCLBIN_UUID:
+		target = (uint64_t)&icap->icap_bitstream_uuid;
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(&icap->icap_lock);
+	return target;
+}
+
+/* Kernel APIs exported from this sub-device driver. */
+static struct xocl_icap_funcs icap_ops = {
+	.reset_axi_gate = platform_reset_axi_gate,
+	.reset_bitstream = icap_reset_bitstream,
+	.download_boot_firmware = icap_download_boot_firmware,
+	.download_bitstream_axlf = icap_download_bitstream_axlf,
+	.ocl_set_freq = icap_ocl_set_freqscaling,
+	.ocl_get_freq = icap_ocl_get_freqscaling,
+	.ocl_update_clock_freq_topology = icap_ocl_update_clock_freq_topology,
+	.ocl_lock_bitstream = icap_lock_bitstream,
+	.ocl_unlock_bitstream = icap_unlock_bitstream,
+	.get_data = icap_get_data,
+};
+
+static ssize_t clock_freq_topology_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct icap *icap = platform_get_drvdata(to_platform_device(dev));
+	ssize_t cnt = 0;
+
+	mutex_lock(&icap->icap_lock);
+	if (ICAP_PRIVILEGED(icap)) {
+		memcpy(buf, icap->icap_clock_freq_topology, icap->icap_clock_freq_topology_length);
+		cnt = icap->icap_clock_freq_topology_length;
+	}
+	mutex_unlock(&icap->icap_lock);
+
+	return cnt;
+
+}
+
+static DEVICE_ATTR_RO(clock_freq_topology);
+
+static ssize_t clock_freqs_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct icap *icap = platform_get_drvdata(to_platform_device(dev));
+	ssize_t cnt = 0;
+	int i;
+	u32 freq_counter, freq, request_in_khz, tolerance;
+
+	mutex_lock(&icap->icap_lock);
+
+	for (i = 0; i < ICAP_MAX_NUM_CLOCKS; i++) {
+		freq = icap_get_ocl_frequency(icap, i);
+		if (!uuid_is_null(&icap->icap_bitstream_uuid)) {
+			freq_counter = icap_get_clock_frequency_counter_khz(icap, i);
+
+			request_in_khz = freq * 1000;
+			tolerance = freq * 50;
+
+			if (abs(freq_counter-request_in_khz) > tolerance)
+				ICAP_INFO(icap, "Frequency mismatch, Should be %u khz, Now is %ukhz",
+					  request_in_khz, freq_counter);
+			cnt += sprintf(buf + cnt, "%d\n", DIV_ROUND_CLOSEST(freq_counter, 1000));
+		} else {
+			cnt += sprintf(buf + cnt, "%d\n", freq);
+		}
+	}
+
+	mutex_unlock(&icap->icap_lock);
+
+	return cnt;
+}
+static DEVICE_ATTR_RO(clock_freqs);
+
+static ssize_t icap_rl_program(struct file *filp, struct kobject *kobj,
+	struct bin_attribute *attr, char *buffer, loff_t off, size_t count)
+{
+	struct XHwIcap_Bit_Header bit_header = { 0 };
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct icap *icap = platform_get_drvdata(to_platform_device(dev));
+	ssize_t ret = count;
+
+	if (off == 0) {
+		if (count < DMA_HWICAP_BITFILE_BUFFER_SIZE) {
+			ICAP_ERR(icap, "count is too small %ld", count);
+			return -EINVAL;
+		}
+
+		if (bitstream_parse_header(icap, buffer,
+			DMA_HWICAP_BITFILE_BUFFER_SIZE, &bit_header)) {
+			ICAP_ERR(icap, "parse header failed");
+			return -EINVAL;
+		}
+
+		icap->bit_length = bit_header.HeaderLength +
+			bit_header.BitstreamLength;
+		icap->bit_buffer = vmalloc(icap->bit_length);
+	}
+
+	if (off + count >= icap->bit_length) {
+		/*
+		 * assumes all subdevices are removed at this time
+		 */
+		memcpy(icap->bit_buffer + off, buffer, icap->bit_length - off);
+		icap_freeze_axi_gate_shell(icap);
+		ret = icap_download(icap, icap->bit_buffer, icap->bit_length);
+		if (ret) {
+			ICAP_ERR(icap, "bitstream download failed");
+			ret = -EIO;
+		} else {
+			ret = count;
+		}
+		icap_free_axi_gate_shell(icap);
+		/* has to reset pci, otherwise firewall trips */
+		xocl_reset(xocl_get_xdev(icap->icap_pdev));
+		icap->icap_bitstream_id = 0;
+		memset(&icap->icap_bitstream_uuid, 0, sizeof(uuid_t));
+		vfree(icap->bit_buffer);
+		icap->bit_buffer = NULL;
+	} else {
+		memcpy(icap->bit_buffer + off, buffer, count);
+	}
+
+	return ret;
+}
+
+static struct bin_attribute shell_program_attr = {
+	.attr = {
+		.name = "shell_program",
+		.mode = 0200
+	},
+	.read = NULL,
+	.write = icap_rl_program,
+	.size = 0
+};
+
+static struct bin_attribute *icap_mgmt_bin_attrs[] = {
+	&shell_program_attr,
+	NULL,
+};
+
+static struct attribute_group icap_mgmt_bin_attr_group = {
+	.bin_attrs = icap_mgmt_bin_attrs,
+};
+
+static ssize_t idcode_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct icap *icap = platform_get_drvdata(to_platform_device(dev));
+	ssize_t cnt = 0;
+	uint32_t val;
+
+	mutex_lock(&icap->icap_lock);
+	if (ICAP_PRIVILEGED(icap)) {
+		cnt = sprintf(buf, "0x%x\n", icap->idcode);
+	} else {
+		icap_read_from_peer(to_platform_device(dev), IDCODE, &val, sizeof(unsigned int));
+		cnt = sprintf(buf, "0x%x\n", val);
+	}
+	mutex_unlock(&icap->icap_lock);
+
+	return cnt;
+}
+
+static DEVICE_ATTR_RO(idcode);
+
+static struct attribute *icap_attrs[] = {
+	&dev_attr_clock_freq_topology.attr,
+	&dev_attr_clock_freqs.attr,
+	&dev_attr_idcode.attr,
+	NULL,
+};
+
+//- Debug IP_layout--
+static ssize_t icap_read_debug_ip_layout(struct file *filp, struct kobject *kobj,
+	struct bin_attribute *attr, char *buffer, loff_t offset, size_t count)
+{
+	struct icap *icap;
+	u32 nread = 0;
+	size_t size = 0;
+
+	icap = (struct icap *)dev_get_drvdata(container_of(kobj, struct device, kobj));
+
+	if (!icap || !icap->debug_layout)
+		return 0;
+
+	mutex_lock(&icap->icap_lock);
+
+	size = sizeof_sect(icap->debug_layout, m_debug_ip_data);
+	if (offset >= size)
+		goto unlock;
+
+	if (count < size - offset)
+		nread = count;
+	else
+		nread = size - offset;
+
+	memcpy(buffer, ((char *)icap->debug_layout) + offset, nread);
+
+unlock:
+	mutex_unlock(&icap->icap_lock);
+	return nread;
+}
+static struct bin_attribute debug_ip_layout_attr = {
+	.attr = {
+		.name = "debug_ip_layout",
+		.mode = 0444
+	},
+	.read = icap_read_debug_ip_layout,
+	.write = NULL,
+	.size = 0
+};
+
+//IP layout
+static ssize_t icap_read_ip_layout(struct file *filp, struct kobject *kobj,
+	struct bin_attribute *attr, char *buffer, loff_t offset, size_t count)
+{
+	struct icap *icap;
+	u32 nread = 0;
+	size_t size = 0;
+
+	icap = (struct icap *)dev_get_drvdata(container_of(kobj, struct device, kobj));
+
+	if (!icap || !icap->ip_layout)
+		return 0;
+
+	mutex_lock(&icap->icap_lock);
+
+	size = sizeof_sect(icap->ip_layout, m_ip_data);
+	if (offset >= size)
+		goto unlock;
+
+	if (count < size - offset)
+		nread = count;
+	else
+		nread = size - offset;
+
+	memcpy(buffer, ((char *)icap->ip_layout) + offset, nread);
+
+unlock:
+	mutex_unlock(&icap->icap_lock);
+	return nread;
+}
+
+static struct bin_attribute ip_layout_attr = {
+	.attr = {
+		.name = "ip_layout",
+		.mode = 0444
+	},
+	.read = icap_read_ip_layout,
+	.write = NULL,
+	.size = 0
+};
+
+//-Connectivity--
+static ssize_t icap_read_connectivity(struct file *filp, struct kobject *kobj,
+	struct bin_attribute *attr, char *buffer, loff_t offset, size_t count)
+{
+	struct icap *icap;
+	u32 nread = 0;
+	size_t size = 0;
+
+	icap = (struct icap *)dev_get_drvdata(container_of(kobj, struct device, kobj));
+
+	if (!icap || !icap->connectivity)
+		return 0;
+
+	mutex_lock(&icap->icap_lock);
+
+	size = sizeof_sect(icap->connectivity, m_connection);
+	if (offset >= size)
+		goto unlock;
+
+	if (count < size - offset)
+		nread = count;
+	else
+		nread = size - offset;
+
+	memcpy(buffer, ((char *)icap->connectivity) + offset, nread);
+
+unlock:
+	mutex_unlock(&icap->icap_lock);
+	return nread;
+}
+
+static struct bin_attribute connectivity_attr = {
+	.attr = {
+		.name = "connectivity",
+		.mode = 0444
+	},
+	.read = icap_read_connectivity,
+	.write = NULL,
+	.size = 0
+};
+
+
+//-Mem_topology--
+static ssize_t icap_read_mem_topology(struct file *filp, struct kobject *kobj,
+	struct bin_attribute *attr, char *buffer, loff_t offset, size_t count)
+{
+	struct icap *icap;
+	u32 nread = 0;
+	size_t size = 0;
+
+	icap = (struct icap *)dev_get_drvdata(container_of(kobj, struct device, kobj));
+
+	if (!icap || !icap->mem_topo)
+		return 0;
+
+	mutex_lock(&icap->icap_lock);
+
+	size = sizeof_sect(icap->mem_topo, m_mem_data);
+	if (offset >= size)
+		goto unlock;
+
+	if (count < size - offset)
+		nread = count;
+	else
+		nread = size - offset;
+
+	memcpy(buffer, ((char *)icap->mem_topo) + offset, nread);
+unlock:
+	mutex_unlock(&icap->icap_lock);
+	return nread;
+}
+
+
+static struct bin_attribute mem_topology_attr = {
+	.attr = {
+		.name = "mem_topology",
+		.mode = 0444
+	},
+	.read = icap_read_mem_topology,
+	.write = NULL,
+	.size = 0
+};
+
+static struct bin_attribute *icap_bin_attrs[] = {
+	&debug_ip_layout_attr,
+	&ip_layout_attr,
+	&connectivity_attr,
+	&mem_topology_attr,
+	NULL,
+};
+
+static struct attribute_group icap_attr_group = {
+	.attrs = icap_attrs,
+	.bin_attrs = icap_bin_attrs,
+};
+
+static int icap_remove(struct platform_device *pdev)
+{
+	struct icap *icap = platform_get_drvdata(pdev);
+	int i;
+
+	BUG_ON(icap == NULL);
+
+	del_all_users(icap);
+	xocl_subdev_register(pdev, XOCL_SUBDEV_ICAP, NULL);
+
+	if (ICAP_PRIVILEGED(icap))
+		sysfs_remove_group(&pdev->dev.kobj, &icap_mgmt_bin_attr_group);
+
+	if (icap->bit_buffer)
+		vfree(icap->bit_buffer);
+
+	iounmap(icap->icap_regs);
+	iounmap(icap->icap_state);
+	iounmap(icap->icap_axi_gate);
+	for (i = 0; i < ICAP_MAX_NUM_CLOCKS; i++)
+		iounmap(icap->icap_clock_bases[i]);
+	free_clear_bitstream(icap);
+	free_clock_freq_topology(icap);
+
+	sysfs_remove_group(&pdev->dev.kobj, &icap_attr_group);
+
+	ICAP_INFO(icap, "cleaned up successfully");
+	platform_set_drvdata(pdev, NULL);
+	vfree(icap->mem_topo);
+	vfree(icap->ip_layout);
+	vfree(icap->debug_layout);
+	vfree(icap->connectivity);
+	kfree(icap);
+	return 0;
+}
+
+/*
+ * Run the following sequence of canned commands to obtain IDCODE of the FPGA
+ */
+static void icap_probe_chip(struct icap *icap)
+{
+	u32 w;
+
+	if (!ICAP_PRIVILEGED(icap))
+		return;
+
+	w = reg_rd(&icap->icap_regs->ir_sr);
+	w = reg_rd(&icap->icap_regs->ir_sr);
+	reg_wr(&icap->icap_regs->ir_gier, 0x0);
+	w = reg_rd(&icap->icap_regs->ir_wfv);
+	reg_wr(&icap->icap_regs->ir_wf, 0xffffffff);
+	reg_wr(&icap->icap_regs->ir_wf, 0xaa995566);
+	reg_wr(&icap->icap_regs->ir_wf, 0x20000000);
+	reg_wr(&icap->icap_regs->ir_wf, 0x20000000);
+	reg_wr(&icap->icap_regs->ir_wf, 0x28018001);
+	reg_wr(&icap->icap_regs->ir_wf, 0x20000000);
+	reg_wr(&icap->icap_regs->ir_wf, 0x20000000);
+	w = reg_rd(&icap->icap_regs->ir_cr);
+	reg_wr(&icap->icap_regs->ir_cr, 0x1);
+	w = reg_rd(&icap->icap_regs->ir_cr);
+	w = reg_rd(&icap->icap_regs->ir_cr);
+	w = reg_rd(&icap->icap_regs->ir_sr);
+	w = reg_rd(&icap->icap_regs->ir_cr);
+	w = reg_rd(&icap->icap_regs->ir_sr);
+	reg_wr(&icap->icap_regs->ir_sz, 0x1);
+	w = reg_rd(&icap->icap_regs->ir_cr);
+	reg_wr(&icap->icap_regs->ir_cr, 0x2);
+	w = reg_rd(&icap->icap_regs->ir_rfo);
+	icap->idcode = reg_rd(&icap->icap_regs->ir_rf);
+	w = reg_rd(&icap->icap_regs->ir_cr);
+}
+
+static int icap_probe(struct platform_device *pdev)
+{
+	struct icap *icap = NULL;
+	struct resource *res;
+	int ret;
+	int reg_grp;
+	void **regs;
+
+	icap = kzalloc(sizeof(struct icap), GFP_KERNEL);
+	if (!icap)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, icap);
+	icap->icap_pdev = pdev;
+	mutex_init(&icap->icap_lock);
+	INIT_LIST_HEAD(&icap->icap_bitstream_users);
+
+	for (reg_grp = 0; reg_grp < ICAP_MAX_REG_GROUPS; reg_grp++) {
+		switch (reg_grp) {
+		case 0:
+			regs = (void **)&icap->icap_regs;
+			break;
+		case 1:
+			regs = (void **)&icap->icap_state;
+			break;
+		case 2:
+			regs = (void **)&icap->icap_axi_gate;
+			break;
+		case 3:
+			regs = (void **)&icap->icap_clock_bases[0];
+			break;
+		case 4:
+			regs = (void **)&icap->icap_clock_bases[1];
+			break;
+		case 5:
+			regs = (void **)&icap->icap_clock_freq_counter;
+			break;
+		default:
+			BUG();
+			break;
+		}
+		res = platform_get_resource(pdev, IORESOURCE_MEM, reg_grp);
+		if (res != NULL) {
+			*regs = ioremap_nocache(res->start,
+				res->end - res->start + 1);
+			if (*regs == NULL) {
+				ICAP_ERR(icap,
+					"failed to map in register group: %d",
+					reg_grp);
+				ret = -EIO;
+				goto failed;
+			} else {
+				ICAP_INFO(icap,
+					"mapped in register group %d @ 0x%p",
+					reg_grp, *regs);
+			}
+		} else {
+			if (reg_grp != 0) {
+				ICAP_ERR(icap,
+					"failed to find register group: %d",
+					reg_grp);
+				ret = -EIO;
+				goto failed;
+			}
+			break;
+		}
+	}
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &icap_attr_group);
+	if (ret) {
+		ICAP_ERR(icap, "create icap attrs failed: %d", ret);
+		goto failed;
+	}
+
+	if (ICAP_PRIVILEGED(icap)) {
+		ret = sysfs_create_group(&pdev->dev.kobj,
+			&icap_mgmt_bin_attr_group);
+		if (ret) {
+			ICAP_ERR(icap, "create icap attrs failed: %d", ret);
+			goto failed;
+		}
+	}
+
+	icap_probe_chip(icap);
+	if (!ICAP_PRIVILEGED(icap))
+		icap_unlock_bitstream(pdev, NULL, 0);
+	ICAP_INFO(icap, "successfully initialized FPGA IDCODE 0x%x",
+			icap->idcode);
+	xocl_subdev_register(pdev, XOCL_SUBDEV_ICAP, &icap_ops);
+	return 0;
+
+failed:
+	(void) icap_remove(pdev);
+	return ret;
+}
+
+
+struct platform_device_id icap_id_table[] = {
+	{ XOCL_ICAP, 0 },
+	{ },
+};
+
+static struct platform_driver icap_driver = {
+	.probe		= icap_probe,
+	.remove		= icap_remove,
+	.driver		= {
+		.name	= XOCL_ICAP,
+	},
+	.id_table = icap_id_table,
+};
+
+int __init xocl_init_icap(void)
+{
+	return platform_driver_register(&icap_driver);
+}
+
+void xocl_fini_icap(void)
+{
+	platform_driver_unregister(&icap_driver);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/mailbox.c b/drivers/gpu/drm/xocl/subdev/mailbox.c
new file mode 100644
index 000000000000..dc4736c9100a
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/mailbox.c
@@ -0,0 +1,1868 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2016-2018 Xilinx, Inc. All rights reserved.
+ *
+ * Authors: Max Zhen <maxz@xilinx.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Statement of Theory
+ *
+ * This is the mailbox sub-device driver added into existing xclmgmt / xocl
+ * driver so that user pf and mgmt pf can send and receive messages of
+ * arbitrary length to / from peer. The driver is written based on the spec of
+ * pg114 document (https://www.xilinx.com/support/documentation/
+ * ip_documentation/mailbox/v2_1/pg114-mailbox.pdf). The HW provides one TX
+ * channel and one RX channel, which operate completely independent of each
+ * other. Data can be pushed into or read from a channel in DWORD unit as a
+ * FIFO.
+ *
+ *
+ * Packet layer
+ *
+ * The driver implemented two transport layers - packet and message layer (see
+ * below). A packet is a fixed size chunk of data that can be send through TX
+ * channel or retrieved from RX channel. The TX and RX interrupt happens at
+ * packet boundary, instead of DWORD boundary. The driver will not attempt to
+ * send next packet until the previous one is read by peer. Similarly, the
+ * driver will not attempt to read the data from HW until a full packet has been
+ * written to HW by peer. No polling is implemented. Data transfer is entirely
+ * interrupt driven. So, the interrupt functionality needs to work and enabled
+ * on both mgmt and user pf for mailbox driver to function properly.
+ *
+ * A TX packet is considered as time'd out after sitting in the TX channel of
+ * mailbox HW for two packet ticks (1 packet tick = 1 second, for now) without
+ * being read by peer. Currently, the driver will not try to re-transmit the
+ * packet after timeout. It just simply propagate the error to the upper layer.
+ * A retry at packet layer can be implement later, if considered as appropriate.
+ *
+ *
+ * Message layer
+ *
+ * A message is a data buffer of arbitrary length. The driver will break a
+ * message into multiple packets and transmit them to the peer, which, in turn,
+ * will assemble them into a full message before it's delivered to upper layer
+ * for further processing. One message requires at least one packet to be
+ * transferred to the peer.
+ *
+ * Each message has a unique temporary u64 ID (see communication model below
+ * for more detail). The ID shows up in each packet's header. So, at packet
+ * layer, there is no assumption that adjacent packets belong to the same
+ * message. However, for the sake of simplicity, at message layer, the driver
+ * will not attempt to send the next message until the sending of current one
+ * is finished. I.E., we implement a FIFO for message TX channel. All messages
+ * are sent by driver in the order of received from upper layer. We can
+ * implement messages of different priority later, if needed. There is no
+ * certain order for receiving messages. It's up to the peer side to decide
+ * which message gets enqueued into its own TX queue first, which will be
+ * received first on the other side.
+ *
+ * A message is considered as time'd out when it's transmit (send or receive)
+ * is not finished within 10 packet ticks. This applies to all messages queued
+ * up on both RX and TX channels. Again, no retry for a time'd out message is
+ * implemented. The error will be simply passed to upper layer. Also, a TX
+ * message may time out earlier if it's being transmitted and one of it's
+ * packets time'd out. During normal operation, timeout should never happen.
+ *
+ * The upper layer can choose to queue a message for TX or RX asynchronously
+ * when it provides a callback or wait synchronously when no callback is
+ * provided.
+ *
+ *
+ * Communication model
+ *
+ * At the highest layer, the driver implements a request-response communication
+ * model. A request may or may not require a response, but a response must match
+ * a request, or it'll be silently dropped. The driver provides a few kernel
+ * APIs for mgmt and user pf to talk to each other in this model (see kernel
+ * APIs section below for details). Each request or response is a message by
+ * itself. A request message will automatically be assigned a message ID when
+ * it's enqueued into TX channel for sending. If this request requires a
+ * response, the buffer provided by caller for receiving response will be
+ * enqueued into RX channel as well. The enqueued response message will have
+ * the same message ID as the corresponding request message. The response
+ * message, if provided, will always be enqueued before the request message is
+ * enqueued to avoid race condition.
+ *
+ * The driver will automatically enqueue a special message into the RX channel
+ * for receiving new request after initialized. This request RX message has a
+ * special message ID (id=0) and never time'd out. When a new request comes
+ * from peer, it'll be copied into request RX message then passed to the
+ * callback provided by upper layer through xocl_peer_listen() API for further
+ * processing. Currently, the driver implements only one kernel thread for RX
+ * channel and one for TX channel. So, all message callback happens in the
+ * context of that channel thread. So, the user of mailbox driver needs to be
+ * careful when it calls xocl_peer_request() synchronously in this context.
+ * You may see deadlock when both ends are trying to call xocl_peer_request()
+ * synchronously at the same time.
+ *
+ *
+ * +------------------+            +------------------+
+ * | Request/Response | <--------> | Request/Response |
+ * +------------------+            +------------------+
+ * | Message          | <--------> | Message          |
+ * +------------------+            +------------------+
+ * | Packet           | <--------> | Packet           |
+ * +------------------+            +------------------+
+ * | RX/TX Channel    | <<======>> | RX/TX Channel    |
+ * +------------------+            +------------------+
+ *   mgmt pf                         user pf
+ */
+
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/crc32c.h>
+#include <linux/random.h>
+#include "../xocl_drv.h"
+
+int mailbox_no_intr;
+module_param(mailbox_no_intr, int, (S_IRUGO|S_IWUSR));
+MODULE_PARM_DESC(mailbox_no_intr,
+	"Disable mailbox interrupt and do timer-driven msg passing");
+
+#define	PACKET_SIZE	16 /* Number of DWORD. */
+
+#define	FLAG_STI	(1 << 0)
+#define	FLAG_RTI	(1 << 1)
+
+#define	STATUS_EMPTY	(1 << 0)
+#define	STATUS_FULL	(1 << 1)
+#define	STATUS_STA	(1 << 2)
+#define	STATUS_RTA	(1 << 3)
+
+#define	MBX_ERR(mbx, fmt, arg...)	\
+	xocl_err(&mbx->mbx_pdev->dev, fmt "\n", ##arg)
+#define	MBX_INFO(mbx, fmt, arg...)	\
+	xocl_info(&mbx->mbx_pdev->dev, fmt "\n", ##arg)
+#define	MBX_DBG(mbx, fmt, arg...)	\
+	xocl_dbg(&mbx->mbx_pdev->dev, fmt "\n", ##arg)
+
+#define	MAILBOX_TIMER	HZ	/* in jiffies */
+#define	MSG_TTL		10	/* in MAILBOX_TIMER */
+#define	TEST_MSG_LEN	128
+
+#define	INVALID_MSG_ID	((u64)-1)
+#define	MSG_FLAG_RESPONSE	(1 << 0)
+#define	MSG_FLAG_REQUEST (1 << 1)
+
+#define MAX_MSG_QUEUE_SZ  (PAGE_SIZE << 16)
+#define MAX_MSG_QUEUE_LEN 5
+
+#define MB_CONN_INIT	(0x1<<0)
+#define MB_CONN_SYN	(0x1<<1)
+#define MB_CONN_ACK	(0x1<<2)
+#define MB_CONN_FIN	(0x1<<3)
+
+/*
+ * Mailbox IP register layout
+ */
+struct mailbox_reg {
+	u32			mbr_wrdata;
+	u32			mbr_resv1;
+	u32			mbr_rddata;
+	u32			mbr_resv2;
+	u32			mbr_status;
+	u32			mbr_error;
+	u32			mbr_sit;
+	u32			mbr_rit;
+	u32			mbr_is;
+	u32			mbr_ie;
+	u32			mbr_ip;
+	u32			mbr_ctrl;
+} __attribute__((packed));
+
+/*
+ * A message transport by mailbox.
+ */
+struct mailbox_msg {
+	struct list_head	mbm_list;
+	struct mailbox_channel	*mbm_ch;
+	u64			mbm_req_id;
+	char			*mbm_data;
+	size_t			mbm_len;
+	int			mbm_error;
+	struct completion	mbm_complete;
+	mailbox_msg_cb_t	mbm_cb;
+	void			*mbm_cb_arg;
+	u32			mbm_flags;
+	int			mbm_ttl;
+	bool			mbm_timer_on;
+};
+
+/*
+ * A packet transport by mailbox.
+ * When extending, only add new data structure to body. Choose to add new flag
+ * if new feature can be safely ignored by peer, other wise, add new type.
+ */
+enum packet_type {
+	PKT_INVALID = 0,
+	PKT_TEST,
+	PKT_MSG_START,
+	PKT_MSG_BODY
+};
+
+
+enum conn_state {
+	CONN_START = 0,
+	CONN_SYN_SENT,
+	CONN_SYN_RECV,
+	CONN_ESTABLISH,
+};
+
+/* Lower 8 bits for type, the rest for flags. */
+#define	PKT_TYPE_MASK		0xff
+#define	PKT_TYPE_MSG_END	(1 << 31)
+struct mailbox_pkt {
+	struct {
+		u32		type;
+		u32		payload_size;
+	} hdr;
+	union {
+		u32		data[PACKET_SIZE - 2];
+		struct {
+			u64	msg_req_id;
+			u32	msg_flags;
+			u32	msg_size;
+			u32	payload[0];
+		} msg_start;
+		struct {
+			u32	payload[0];
+		} msg_body;
+	} body;
+} __attribute__((packed));
+
+/*
+ * Mailbox communication channel.
+ */
+#define MBXCS_BIT_READY		0
+#define MBXCS_BIT_STOP		1
+#define MBXCS_BIT_TICK		2
+#define MBXCS_BIT_CHK_STALL	3
+#define MBXCS_BIT_POLL_MODE	4
+
+struct mailbox_channel;
+typedef	void (*chan_func_t)(struct mailbox_channel *ch);
+struct mailbox_channel {
+	struct mailbox		*mbc_parent;
+	char			*mbc_name;
+
+	struct workqueue_struct	*mbc_wq;
+	struct work_struct	mbc_work;
+	struct completion	mbc_worker;
+	chan_func_t		mbc_tran;
+	unsigned long		mbc_state;
+
+	struct mutex		mbc_mutex;
+	struct list_head	mbc_msgs;
+
+	struct mailbox_msg	*mbc_cur_msg;
+	int			mbc_bytes_done;
+	struct mailbox_pkt	mbc_packet;
+
+	struct timer_list	mbc_timer;
+	bool			mbc_timer_on;
+};
+
+/*
+ * The mailbox softstate.
+ */
+struct mailbox {
+	struct platform_device	*mbx_pdev;
+	struct mailbox_reg	*mbx_regs;
+	u32			mbx_irq;
+
+	struct mailbox_channel	mbx_rx;
+	struct mailbox_channel	mbx_tx;
+
+	/* For listening to peer's request. */
+	mailbox_msg_cb_t	mbx_listen_cb;
+	void			*mbx_listen_cb_arg;
+	struct workqueue_struct	*mbx_listen_wq;
+	struct work_struct	mbx_listen_worker;
+
+	int			mbx_paired;
+	/*
+	 * For testing basic intr and mailbox comm functionality via sysfs.
+	 * No locking protection, use with care.
+	 */
+	struct mailbox_pkt	mbx_tst_pkt;
+	char			mbx_tst_tx_msg[TEST_MSG_LEN];
+	char			mbx_tst_rx_msg[TEST_MSG_LEN];
+	size_t			mbx_tst_tx_msg_len;
+
+	/* Req list for all incoming request message */
+	struct completion mbx_comp;
+	struct mutex mbx_lock;
+	struct list_head mbx_req_list;
+	uint8_t mbx_req_cnt;
+	size_t mbx_req_sz;
+
+	struct mutex mbx_conn_lock;
+	uint64_t mbx_conn_id;
+	enum conn_state mbx_state;
+	bool mbx_established;
+	uint32_t mbx_prot_ver;
+
+	void *mbx_kaddr;
+};
+
+static inline const char *reg2name(struct mailbox *mbx, u32 *reg)
+{
+	static const char *reg_names[] = {
+		"wrdata",
+		"reserved1",
+		"rddata",
+		"reserved2",
+		"status",
+		"error",
+		"sit",
+		"rit",
+		"is",
+		"ie",
+		"ip",
+		"ctrl"
+	};
+
+	return reg_names[((uintptr_t)reg -
+		(uintptr_t)mbx->mbx_regs) / sizeof(u32)];
+}
+
+struct mailbox_conn {
+	uint64_t flag;
+	void *kaddr;
+	phys_addr_t paddr;
+	uint32_t crc32;
+	uint32_t ver;
+	uint64_t sec_id;
+};
+
+int mailbox_request(struct platform_device *pdev, void *req, size_t reqlen,
+		    void *resp, size_t *resplen, mailbox_msg_cb_t cb, void *cbarg);
+int mailbox_post(struct platform_device *pdev, u64 reqid, void *buf, size_t len);
+static int mailbox_connect_status(struct platform_device *pdev);
+static void connect_state_handler(struct mailbox *mbx, struct mailbox_conn *conn);
+
+static void connect_state_touch(struct mailbox *mbx, uint64_t flag)
+{
+	struct mailbox_conn conn = {0};
+	if (!mbx)
+		return;
+	conn.flag = flag;
+	connect_state_handler(mbx, &conn);
+}
+
+
+static inline u32 mailbox_reg_rd(struct mailbox *mbx, u32 *reg)
+{
+	u32 val = ioread32(reg);
+
+#ifdef	MAILBOX_REG_DEBUG
+	MBX_DBG(mbx, "REG_RD(%s)=0x%x", reg2name(mbx, reg), val);
+#endif
+	return val;
+}
+
+static inline void mailbox_reg_wr(struct mailbox *mbx, u32 *reg, u32 val)
+{
+#ifdef	MAILBOX_REG_DEBUG
+	MBX_DBG(mbx, "REG_WR(%s, 0x%x)", reg2name(mbx, reg), val);
+#endif
+	iowrite32(val, reg);
+}
+
+static inline void reset_pkt(struct mailbox_pkt *pkt)
+{
+	pkt->hdr.type = PKT_INVALID;
+}
+
+static inline bool valid_pkt(struct mailbox_pkt *pkt)
+{
+	return (pkt->hdr.type != PKT_INVALID);
+}
+
+irqreturn_t mailbox_isr(int irq, void *arg)
+{
+	struct mailbox *mbx = (struct mailbox *)arg;
+	u32 is = mailbox_reg_rd(mbx, &mbx->mbx_regs->mbr_is);
+
+	while (is) {
+		MBX_DBG(mbx, "intr status: 0x%x", is);
+
+		if ((is & FLAG_STI) != 0) {
+			/* A packet has been sent successfully. */
+			complete(&mbx->mbx_tx.mbc_worker);
+		}
+		if ((is & FLAG_RTI) != 0) {
+			/* A packet is waiting to be received from mailbox. */
+			complete(&mbx->mbx_rx.mbc_worker);
+		}
+		/* Anything else is not expected. */
+		if ((is & (FLAG_STI | FLAG_RTI)) == 0) {
+			MBX_ERR(mbx, "spurious mailbox irq %d, is=0x%x",
+				irq, is);
+		}
+
+		/* Clear intr state for receiving next one. */
+		mailbox_reg_wr(mbx, &mbx->mbx_regs->mbr_is, is);
+
+		is = mailbox_reg_rd(mbx, &mbx->mbx_regs->mbr_is);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void chan_timer(struct timer_list *t)
+{
+	struct mailbox_channel *ch = from_timer(ch, t, mbc_timer);
+
+	MBX_DBG(ch->mbc_parent, "%s tick", ch->mbc_name);
+
+	set_bit(MBXCS_BIT_TICK, &ch->mbc_state);
+	complete(&ch->mbc_worker);
+
+	/* We're a periodic timer. */
+	mod_timer(&ch->mbc_timer, jiffies + MAILBOX_TIMER);
+}
+
+static void chan_config_timer(struct mailbox_channel *ch)
+{
+	struct list_head *pos, *n;
+	struct mailbox_msg *msg = NULL;
+	bool on = false;
+
+	mutex_lock(&ch->mbc_mutex);
+
+	if (test_bit(MBXCS_BIT_POLL_MODE, &ch->mbc_state)) {
+		on = true;
+	} else {
+		list_for_each_safe(pos, n, &ch->mbc_msgs) {
+			msg = list_entry(pos, struct mailbox_msg, mbm_list);
+			if (msg->mbm_req_id == 0)
+			       continue;
+			on = true;
+			break;
+		}
+	}
+
+	if (on != ch->mbc_timer_on) {
+		ch->mbc_timer_on = on;
+		if (on)
+			mod_timer(&ch->mbc_timer, jiffies + MAILBOX_TIMER);
+		else
+			del_timer_sync(&ch->mbc_timer);
+	}
+
+	mutex_unlock(&ch->mbc_mutex);
+}
+
+static void free_msg(struct mailbox_msg *msg)
+{
+	vfree(msg);
+}
+
+static void msg_done(struct mailbox_msg *msg, int err)
+{
+	struct mailbox_channel *ch = msg->mbm_ch;
+	struct mailbox *mbx = ch->mbc_parent;
+
+	MBX_DBG(ch->mbc_parent, "%s finishing msg id=0x%llx err=%d",
+		ch->mbc_name, msg->mbm_req_id, err);
+
+	msg->mbm_error = err;
+	if (msg->mbm_cb) {
+		msg->mbm_cb(msg->mbm_cb_arg, msg->mbm_data, msg->mbm_len,
+			msg->mbm_req_id, msg->mbm_error);
+		free_msg(msg);
+	} else {
+		if (msg->mbm_flags & MSG_FLAG_REQUEST) {
+			if ((mbx->mbx_req_sz+msg->mbm_len) >= MAX_MSG_QUEUE_SZ ||
+				  mbx->mbx_req_cnt >= MAX_MSG_QUEUE_LEN) {
+				goto done;
+			}
+			mutex_lock(&ch->mbc_parent->mbx_lock);
+			list_add_tail(&msg->mbm_list, &ch->mbc_parent->mbx_req_list);
+			mbx->mbx_req_cnt++;
+			mbx->mbx_req_sz += msg->mbm_len;
+			mutex_unlock(&ch->mbc_parent->mbx_lock);
+
+			complete(&ch->mbc_parent->mbx_comp);
+		} else {
+			complete(&msg->mbm_complete);
+		}
+	}
+done:
+	chan_config_timer(ch);
+}
+
+static void chan_msg_done(struct mailbox_channel *ch, int err)
+{
+	if (!ch->mbc_cur_msg)
+		return;
+
+	msg_done(ch->mbc_cur_msg, err);
+	ch->mbc_cur_msg = NULL;
+	ch->mbc_bytes_done = 0;
+}
+
+void timeout_msg(struct mailbox_channel *ch)
+{
+	struct mailbox *mbx = ch->mbc_parent;
+	struct mailbox_msg *msg = NULL;
+	struct list_head *pos, *n;
+	struct list_head l = LIST_HEAD_INIT(l);
+	bool reschedule = false;
+
+	/* Check active msg first. */
+	msg = ch->mbc_cur_msg;
+	if (msg) {
+
+		if (msg->mbm_ttl == 0) {
+			MBX_ERR(mbx, "found active msg time'd out");
+			chan_msg_done(ch, -ETIME);
+		} else {
+			if (msg->mbm_timer_on) {
+				msg->mbm_ttl--;
+				/* Need to come back again for this one. */
+				reschedule = true;
+			}
+		}
+	}
+
+	mutex_lock(&ch->mbc_mutex);
+
+	list_for_each_safe(pos, n, &ch->mbc_msgs) {
+		msg = list_entry(pos, struct mailbox_msg, mbm_list);
+		if (!msg->mbm_timer_on)
+			continue;
+		if (msg->mbm_req_id == 0)
+		       continue;
+		if (msg->mbm_ttl == 0) {
+			list_del(&msg->mbm_list);
+			list_add_tail(&msg->mbm_list, &l);
+		} else {
+			msg->mbm_ttl--;
+			/* Need to come back again for this one. */
+			reschedule = true;
+		}
+	}
+
+	mutex_unlock(&ch->mbc_mutex);
+
+	if (!list_empty(&l))
+		MBX_ERR(mbx, "found waiting msg time'd out");
+
+	list_for_each_safe(pos, n, &l) {
+		msg = list_entry(pos, struct mailbox_msg, mbm_list);
+		list_del(&msg->mbm_list);
+		msg_done(msg, -ETIME);
+	}
+}
+
+static void chann_worker(struct work_struct *work)
+{
+	struct mailbox_channel *ch =
+		container_of(work, struct mailbox_channel, mbc_work);
+	struct mailbox *mbx = ch->mbc_parent;
+
+	while (!test_bit(MBXCS_BIT_STOP, &ch->mbc_state)) {
+		MBX_DBG(mbx, "%s worker start", ch->mbc_name);
+		ch->mbc_tran(ch);
+		wait_for_completion_interruptible(&ch->mbc_worker);
+	}
+}
+
+static inline u32 mailbox_chk_err(struct mailbox *mbx)
+{
+	u32 val = mailbox_reg_rd(mbx, &mbx->mbx_regs->mbr_error);
+
+	/* Ignore bad register value after firewall is tripped. */
+	if (val == 0xffffffff)
+		val = 0;
+
+	/* Error should not be seen, shout when found. */
+	if (val)
+		MBX_ERR(mbx, "mailbox error detected, error=0x%x\n", val);
+	return val;
+}
+
+static int chan_msg_enqueue(struct mailbox_channel *ch, struct mailbox_msg *msg)
+{
+	int rv = 0;
+
+	MBX_DBG(ch->mbc_parent, "%s enqueuing msg, id=0x%llx\n",
+		ch->mbc_name, msg->mbm_req_id);
+
+	BUG_ON(msg->mbm_req_id == INVALID_MSG_ID);
+
+	mutex_lock(&ch->mbc_mutex);
+	if (test_bit(MBXCS_BIT_STOP, &ch->mbc_state)) {
+		rv = -ESHUTDOWN;
+	} else {
+		list_add_tail(&msg->mbm_list, &ch->mbc_msgs);
+		msg->mbm_ch = ch;
+//		msg->mbm_ttl = MSG_TTL;
+	}
+	mutex_unlock(&ch->mbc_mutex);
+
+	chan_config_timer(ch);
+
+	return rv;
+}
+
+static struct mailbox_msg *chan_msg_dequeue(struct mailbox_channel *ch,
+	u64 req_id)
+{
+	struct mailbox_msg *msg = NULL;
+	struct list_head *pos;
+
+	mutex_lock(&ch->mbc_mutex);
+
+	/* Take the first msg. */
+	if (req_id == INVALID_MSG_ID) {
+		msg = list_first_entry_or_null(&ch->mbc_msgs,
+			struct mailbox_msg, mbm_list);
+	/* Take the msg w/ specified ID. */
+	} else {
+		list_for_each(pos, &ch->mbc_msgs) {
+			msg = list_entry(pos, struct mailbox_msg, mbm_list);
+			if (msg->mbm_req_id == req_id)
+				break;
+		}
+	}
+
+	if (msg) {
+		MBX_DBG(ch->mbc_parent, "%s dequeued msg, id=0x%llx\n",
+			ch->mbc_name, msg->mbm_req_id);
+		list_del(&msg->mbm_list);
+	}
+
+	mutex_unlock(&ch->mbc_mutex);
+
+	return msg;
+}
+
+static struct mailbox_msg *alloc_msg(void *buf, size_t len)
+{
+	char *newbuf = NULL;
+	struct mailbox_msg *msg = NULL;
+	/* Give MB*2 secs as time to live */
+	int calculated_ttl = (len >> 19) < MSG_TTL ? MSG_TTL : (len >> 19);
+
+	if (!buf) {
+		msg = vzalloc(sizeof(struct mailbox_msg) + len);
+		if (!msg)
+			return NULL;
+		newbuf = ((char *)msg) + sizeof(struct mailbox_msg);
+	} else {
+		msg = vzalloc(sizeof(struct mailbox_msg));
+		if (!msg)
+			return NULL;
+		newbuf = buf;
+	}
+
+	INIT_LIST_HEAD(&msg->mbm_list);
+	msg->mbm_data = newbuf;
+	msg->mbm_len = len;
+	msg->mbm_ttl = calculated_ttl;
+	msg->mbm_timer_on = false;
+	init_completion(&msg->mbm_complete);
+
+	return msg;
+}
+
+static int chan_init(struct mailbox *mbx, char *nm,
+	struct mailbox_channel *ch, chan_func_t fn)
+{
+	ch->mbc_parent = mbx;
+	ch->mbc_name = nm;
+	ch->mbc_tran = fn;
+	INIT_LIST_HEAD(&ch->mbc_msgs);
+	init_completion(&ch->mbc_worker);
+	mutex_init(&ch->mbc_mutex);
+
+	ch->mbc_cur_msg = NULL;
+	ch->mbc_bytes_done = 0;
+
+	reset_pkt(&ch->mbc_packet);
+	set_bit(MBXCS_BIT_READY, &ch->mbc_state);
+
+	/* One thread for one channel. */
+	ch->mbc_wq =
+		create_singlethread_workqueue(dev_name(&mbx->mbx_pdev->dev));
+	if (!ch->mbc_wq) {
+		ch->mbc_parent = NULL;
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&ch->mbc_work, chann_worker);
+	queue_work(ch->mbc_wq, &ch->mbc_work);
+
+	/* One timer for one channel. */
+	timer_setup(&ch->mbc_timer, chan_timer, 0);
+
+	return 0;
+}
+
+static void chan_fini(struct mailbox_channel *ch)
+{
+	struct mailbox_msg *msg;
+
+	if (!ch->mbc_parent)
+		return;
+
+	/*
+	 * Holding mutex to ensure no new msg is enqueued after
+	 * flag is set.
+	 */
+	mutex_lock(&ch->mbc_mutex);
+	set_bit(MBXCS_BIT_STOP, &ch->mbc_state);
+	mutex_unlock(&ch->mbc_mutex);
+
+	complete(&ch->mbc_worker);
+	cancel_work_sync(&ch->mbc_work);
+	destroy_workqueue(ch->mbc_wq);
+
+	msg = ch->mbc_cur_msg;
+	if (msg)
+		chan_msg_done(ch, -ESHUTDOWN);
+
+	while ((msg = chan_msg_dequeue(ch, INVALID_MSG_ID)) != NULL)
+		msg_done(msg, -ESHUTDOWN);
+
+	del_timer_sync(&ch->mbc_timer);
+}
+
+static void listen_wq_fini(struct mailbox *mbx)
+{
+	BUG_ON(mbx == NULL);
+
+	if (mbx->mbx_listen_wq != NULL) {
+		complete(&mbx->mbx_comp);
+		cancel_work_sync(&mbx->mbx_listen_worker);
+		destroy_workqueue(mbx->mbx_listen_wq);
+	}
+
+}
+
+static void chan_recv_pkt(struct mailbox_channel *ch)
+{
+	int i, retry = 10;
+	struct mailbox *mbx = ch->mbc_parent;
+	struct mailbox_pkt *pkt = &ch->mbc_packet;
+
+	BUG_ON(valid_pkt(pkt));
+
+	/* Picking up a packet from HW. */
+	for (i = 0; i < PACKET_SIZE; i++) {
+		while ((mailbox_reg_rd(mbx,
+			&mbx->mbx_regs->mbr_status) & STATUS_EMPTY) &&
+			(retry-- > 0))
+			msleep(100);
+
+		*(((u32 *)pkt) + i) =
+			mailbox_reg_rd(mbx, &mbx->mbx_regs->mbr_rddata);
+	}
+
+	if ((mailbox_chk_err(mbx) & STATUS_EMPTY) != 0)
+		reset_pkt(pkt);
+	else
+		MBX_DBG(mbx, "received pkt: type=0x%x", pkt->hdr.type);
+}
+
+static void chan_send_pkt(struct mailbox_channel *ch)
+{
+	int i;
+	struct mailbox *mbx = ch->mbc_parent;
+	struct mailbox_pkt *pkt = &ch->mbc_packet;
+
+	BUG_ON(!valid_pkt(pkt));
+
+	MBX_DBG(mbx, "sending pkt: type=0x%x", pkt->hdr.type);
+
+	/* Pushing a packet into HW. */
+	for (i = 0; i < PACKET_SIZE; i++) {
+		mailbox_reg_wr(mbx, &mbx->mbx_regs->mbr_wrdata,
+			*(((u32 *)pkt) + i));
+	}
+
+	reset_pkt(pkt);
+	if (ch->mbc_cur_msg)
+		ch->mbc_bytes_done += ch->mbc_packet.hdr.payload_size;
+
+	BUG_ON((mailbox_chk_err(mbx) & STATUS_FULL) != 0);
+}
+
+static int chan_pkt2msg(struct mailbox_channel *ch)
+{
+	struct mailbox *mbx = ch->mbc_parent;
+	void *msg_data, *pkt_data;
+	struct mailbox_msg *msg = ch->mbc_cur_msg;
+	struct mailbox_pkt *pkt = &ch->mbc_packet;
+	size_t cnt = pkt->hdr.payload_size;
+	u32 type = (pkt->hdr.type & PKT_TYPE_MASK);
+
+	BUG_ON(((type != PKT_MSG_START) && (type != PKT_MSG_BODY)) || !msg);
+
+	if (type == PKT_MSG_START) {
+		msg->mbm_req_id = pkt->body.msg_start.msg_req_id;
+		BUG_ON(msg->mbm_len < pkt->body.msg_start.msg_size);
+		msg->mbm_len = pkt->body.msg_start.msg_size;
+		pkt_data = pkt->body.msg_start.payload;
+	} else {
+		pkt_data = pkt->body.msg_body.payload;
+	}
+
+	if (cnt > msg->mbm_len - ch->mbc_bytes_done) {
+		MBX_ERR(mbx, "invalid mailbox packet size\n");
+		return -EBADMSG;
+	}
+
+	msg_data = msg->mbm_data + ch->mbc_bytes_done;
+	(void) memcpy(msg_data, pkt_data, cnt);
+	ch->mbc_bytes_done += cnt;
+
+	reset_pkt(pkt);
+	return 0;
+}
+
+/*
+ * Worker for RX channel.
+ */
+static void chan_do_rx(struct mailbox_channel *ch)
+{
+	struct mailbox *mbx = ch->mbc_parent;
+	struct mailbox_pkt *pkt = &ch->mbc_packet;
+	struct mailbox_msg *msg = NULL;
+	bool needs_read = false;
+	u64 id = 0;
+	bool eom;
+	int err;
+	u32 type;
+	u32 st = mailbox_reg_rd(mbx, &mbx->mbx_regs->mbr_status);
+
+	/* Check if a packet is ready for reading. */
+	if (st == 0xffffffff) {
+		/* Device is still being reset. */
+		needs_read = false;
+	} else if (test_bit(MBXCS_BIT_POLL_MODE, &ch->mbc_state)) {
+		needs_read = ((st & STATUS_EMPTY) == 0);
+	} else {
+		needs_read = ((st & STATUS_RTA) != 0);
+	}
+
+	if (needs_read) {
+		chan_recv_pkt(ch);
+		type = pkt->hdr.type & PKT_TYPE_MASK;
+		eom = ((pkt->hdr.type & PKT_TYPE_MSG_END) != 0);
+
+		switch (type) {
+		case PKT_TEST:
+			(void) memcpy(&mbx->mbx_tst_pkt, &ch->mbc_packet,
+				sizeof(struct mailbox_pkt));
+			reset_pkt(pkt);
+			return;
+		case PKT_MSG_START:
+			if (ch->mbc_cur_msg) {
+				MBX_ERR(mbx, "received partial msg\n");
+				chan_msg_done(ch, -EBADMSG);
+			}
+
+			/* Get a new active msg. */
+			id = 0;
+			if (pkt->body.msg_start.msg_flags & MSG_FLAG_RESPONSE)
+				id = pkt->body.msg_start.msg_req_id;
+			ch->mbc_cur_msg = chan_msg_dequeue(ch, id);
+
+			if (!ch->mbc_cur_msg) {
+				//no msg, alloc dynamically
+				msg = alloc_msg(NULL, pkt->body.msg_start.msg_size);
+
+				msg->mbm_ch = ch;
+				msg->mbm_flags |= MSG_FLAG_REQUEST;
+				ch->mbc_cur_msg = msg;
+
+			}	else if (pkt->body.msg_start.msg_size >
+				ch->mbc_cur_msg->mbm_len) {
+				chan_msg_done(ch, -EMSGSIZE);
+				MBX_ERR(mbx, "received msg is too big");
+				reset_pkt(pkt);
+			}
+			break;
+		case PKT_MSG_BODY:
+			if (!ch->mbc_cur_msg) {
+				MBX_ERR(mbx, "got unexpected msg body pkt\n");
+				reset_pkt(pkt);
+			}
+			break;
+		default:
+			MBX_ERR(mbx, "invalid mailbox pkt type\n");
+			reset_pkt(pkt);
+			return;
+		}
+
+		if (valid_pkt(pkt)) {
+			err = chan_pkt2msg(ch);
+			if (err || eom)
+				chan_msg_done(ch, err);
+		}
+	}
+
+	/* Handle timer event. */
+	if (test_bit(MBXCS_BIT_TICK, &ch->mbc_state)) {
+		timeout_msg(ch);
+		clear_bit(MBXCS_BIT_TICK, &ch->mbc_state);
+	}
+}
+
+static void chan_msg2pkt(struct mailbox_channel *ch)
+{
+	size_t cnt = 0;
+	size_t payload_off = 0;
+	void *msg_data, *pkt_data;
+	struct mailbox_msg *msg = ch->mbc_cur_msg;
+	struct mailbox_pkt *pkt = &ch->mbc_packet;
+	bool is_start = (ch->mbc_bytes_done == 0);
+	bool is_eom = false;
+
+	if (is_start) {
+		payload_off = offsetof(struct mailbox_pkt,
+			body.msg_start.payload);
+	} else {
+		payload_off = offsetof(struct mailbox_pkt,
+			body.msg_body.payload);
+	}
+	cnt = PACKET_SIZE * sizeof(u32) - payload_off;
+	if (cnt >= msg->mbm_len - ch->mbc_bytes_done) {
+		cnt = msg->mbm_len - ch->mbc_bytes_done;
+		is_eom = true;
+	}
+
+	pkt->hdr.type = is_start ? PKT_MSG_START : PKT_MSG_BODY;
+	pkt->hdr.type |= is_eom ? PKT_TYPE_MSG_END : 0;
+	pkt->hdr.payload_size = cnt;
+
+	if (is_start) {
+		pkt->body.msg_start.msg_req_id = msg->mbm_req_id;
+		pkt->body.msg_start.msg_size = msg->mbm_len;
+		pkt->body.msg_start.msg_flags = msg->mbm_flags;
+		pkt_data = pkt->body.msg_start.payload;
+	} else {
+		pkt_data = pkt->body.msg_body.payload;
+	}
+	msg_data = msg->mbm_data + ch->mbc_bytes_done;
+	(void) memcpy(pkt_data, msg_data, cnt);
+}
+
+static void check_tx_stall(struct mailbox_channel *ch)
+{
+	struct mailbox *mbx = ch->mbc_parent;
+	struct mailbox_msg *msg = ch->mbc_cur_msg;
+
+	/*
+	 * No stall checking in polling mode. Don't know how often peer will
+	 * check the channel.
+	 */
+	if ((msg == NULL) || test_bit(MBXCS_BIT_POLL_MODE, &ch->mbc_state))
+		return;
+
+	/*
+	 * No tx intr has come since last check.
+	 * The TX channel is stalled, reset it.
+	 */
+	if (test_bit(MBXCS_BIT_CHK_STALL, &ch->mbc_state)) {
+		MBX_ERR(mbx, "TX channel stall detected, reset...\n");
+		mailbox_reg_wr(mbx, &mbx->mbx_regs->mbr_ctrl, 0x1);
+		chan_msg_done(ch, -ETIME);
+		connect_state_touch(mbx, MB_CONN_FIN);
+	/* Mark it for next check. */
+	} else {
+		set_bit(MBXCS_BIT_CHK_STALL, &ch->mbc_state);
+	}
+}
+
+
+
+static void rx_enqueued_msg_timer_on(struct mailbox *mbx, uint64_t req_id)
+{
+	struct list_head *pos, *n;
+	struct mailbox_msg *msg = NULL;
+	struct mailbox_channel *ch = NULL;
+	ch = &mbx->mbx_rx;
+	MBX_DBG(mbx, "try to set ch rx, req_id %llu\n", req_id);
+	mutex_lock(&ch->mbc_mutex);
+
+	list_for_each_safe(pos, n, &ch->mbc_msgs) {
+		msg = list_entry(pos, struct mailbox_msg, mbm_list);
+		if (msg->mbm_req_id == req_id) {
+			msg->mbm_timer_on = true;
+			MBX_DBG(mbx, "set ch rx, req_id %llu\n", req_id);
+			break;
+		}
+	}
+
+	mutex_unlock(&ch->mbc_mutex);
+
+}
+
+/*
+ * Worker for TX channel.
+ */
+static void chan_do_tx(struct mailbox_channel *ch)
+{
+	struct mailbox *mbx = ch->mbc_parent;
+	u32 st = mailbox_reg_rd(mbx, &mbx->mbx_regs->mbr_status);
+
+	/* Check if a packet has been read by peer. */
+	if ((st != 0xffffffff) && ((st & STATUS_STA) != 0)) {
+		clear_bit(MBXCS_BIT_CHK_STALL, &ch->mbc_state);
+
+		/*
+		 * The mailbox is free for sending new pkt now. See if we
+		 * have something to send.
+		 */
+
+		/* Finished sending a whole msg, call it done. */
+		if (ch->mbc_cur_msg &&
+			(ch->mbc_cur_msg->mbm_len == ch->mbc_bytes_done)) {
+			rx_enqueued_msg_timer_on(mbx, ch->mbc_cur_msg->mbm_req_id);
+			chan_msg_done(ch, 0);
+		}
+
+		if (!ch->mbc_cur_msg) {
+			ch->mbc_cur_msg = chan_msg_dequeue(ch, INVALID_MSG_ID);
+			if (ch->mbc_cur_msg)
+				ch->mbc_cur_msg->mbm_timer_on = true;
+		}
+
+		if (ch->mbc_cur_msg) {
+			chan_msg2pkt(ch);
+		} else if (valid_pkt(&mbx->mbx_tst_pkt)) {
+			(void) memcpy(&ch->mbc_packet, &mbx->mbx_tst_pkt,
+				sizeof(struct mailbox_pkt));
+			reset_pkt(&mbx->mbx_tst_pkt);
+		} else {
+			return; /* Nothing to send. */
+		}
+
+		chan_send_pkt(ch);
+	}
+
+	/* Handle timer event. */
+	if (test_bit(MBXCS_BIT_TICK, &ch->mbc_state)) {
+		timeout_msg(ch);
+		check_tx_stall(ch);
+		clear_bit(MBXCS_BIT_TICK, &ch->mbc_state);
+	}
+}
+
+static int mailbox_connect_status(struct platform_device *pdev)
+{
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	int ret = 0;
+	mutex_lock(&mbx->mbx_lock);
+	ret = mbx->mbx_paired;
+	mutex_unlock(&mbx->mbx_lock);
+	return ret;
+}
+
+static ssize_t mailbox_ctl_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	u32 *reg = (u32 *)mbx->mbx_regs;
+	int r, n;
+	int nreg = sizeof (struct mailbox_reg) / sizeof (u32);
+
+	for (r = 0, n = 0; r < nreg; r++, reg++) {
+		/* Non-status registers. */
+		if ((reg == &mbx->mbx_regs->mbr_resv1)		||
+			(reg == &mbx->mbx_regs->mbr_wrdata)	||
+			(reg == &mbx->mbx_regs->mbr_rddata)	||
+			(reg == &mbx->mbx_regs->mbr_resv2))
+			continue;
+		/* Write-only status register. */
+		if (reg == &mbx->mbx_regs->mbr_ctrl) {
+			n += sprintf(buf + n, "%02ld %10s = --\n",
+				r * sizeof (u32), reg2name(mbx, reg));
+		/* Read-able status register. */
+		} else {
+			n += sprintf(buf + n, "%02ld %10s = 0x%08x\n",
+				r * sizeof (u32), reg2name(mbx, reg),
+				mailbox_reg_rd(mbx, reg));
+		}
+	}
+
+	return n;
+}
+
+static ssize_t mailbox_ctl_store(struct device *dev,
+	struct device_attribute *da, const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	u32 off, val;
+	int nreg = sizeof (struct mailbox_reg) / sizeof (u32);
+	u32 *reg = (u32 *)mbx->mbx_regs;
+
+	if (sscanf(buf, "%d:%d", &off, &val) != 2 || (off % sizeof (u32)) ||
+		!(off >= 0 && off < nreg * sizeof (u32))) {
+		MBX_ERR(mbx, "input should be <reg_offset:reg_val>");
+		return -EINVAL;
+	}
+	reg += off / sizeof (u32);
+
+	mailbox_reg_wr(mbx, reg, val);
+	return count;
+}
+/* HW register level debugging i/f. */
+static DEVICE_ATTR_RW(mailbox_ctl);
+
+static ssize_t mailbox_pkt_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	int ret = 0;
+
+	if (valid_pkt(&mbx->mbx_tst_pkt)) {
+		(void) memcpy(buf, mbx->mbx_tst_pkt.body.data,
+			mbx->mbx_tst_pkt.hdr.payload_size);
+		ret = mbx->mbx_tst_pkt.hdr.payload_size;
+	}
+
+	return ret;
+}
+
+static ssize_t mailbox_pkt_store(struct device *dev,
+	struct device_attribute *da, const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	size_t maxlen = sizeof (mbx->mbx_tst_pkt.body.data);
+
+	if (count > maxlen) {
+		MBX_ERR(mbx, "max input length is %ld", maxlen);
+		return 0;
+	}
+
+	(void) memcpy(mbx->mbx_tst_pkt.body.data, buf, count);
+	mbx->mbx_tst_pkt.hdr.payload_size = count;
+	mbx->mbx_tst_pkt.hdr.type = PKT_TEST;
+	complete(&mbx->mbx_tx.mbc_worker);
+	return count;
+}
+
+/* Packet test i/f. */
+static DEVICE_ATTR_RW(mailbox_pkt);
+
+static ssize_t mailbox_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	struct mailbox_req req;
+	size_t respsz = sizeof (mbx->mbx_tst_rx_msg);
+	int ret = 0;
+
+	req.req = MAILBOX_REQ_TEST_READ;
+	ret = mailbox_request(to_platform_device(dev), &req, sizeof (req),
+		mbx->mbx_tst_rx_msg, &respsz, NULL, NULL);
+	if (ret) {
+		MBX_ERR(mbx, "failed to read test msg from peer: %d", ret);
+	} else if (respsz > 0) {
+		(void) memcpy(buf, mbx->mbx_tst_rx_msg, respsz);
+		ret = respsz;
+	}
+
+	return ret;
+}
+
+static ssize_t mailbox_store(struct device *dev,
+	struct device_attribute *da, const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	size_t maxlen = sizeof (mbx->mbx_tst_tx_msg);
+	struct mailbox_req req = { 0 };
+
+	if (count > maxlen) {
+		MBX_ERR(mbx, "max input length is %ld", maxlen);
+		return 0;
+	}
+
+	(void) memcpy(mbx->mbx_tst_tx_msg, buf, count);
+	mbx->mbx_tst_tx_msg_len = count;
+	req.req = MAILBOX_REQ_TEST_READY;
+	(void) mailbox_post(mbx->mbx_pdev, 0, &req, sizeof (req));
+
+	return count;
+}
+
+/* Msg test i/f. */
+static DEVICE_ATTR_RW(mailbox);
+
+static ssize_t connection_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	int ret;
+	ret = mailbox_connect_status(pdev);
+	return sprintf(buf, "0x%x\n", ret);
+}
+static DEVICE_ATTR_RO(connection);
+
+
+static struct attribute *mailbox_attrs[] = {
+	&dev_attr_mailbox.attr,
+	&dev_attr_mailbox_ctl.attr,
+	&dev_attr_mailbox_pkt.attr,
+	&dev_attr_connection.attr,
+	NULL,
+};
+
+static const struct attribute_group mailbox_attrgroup = {
+	.attrs = mailbox_attrs,
+};
+
+static void dft_req_msg_cb(void *arg, void *data, size_t len, u64 id, int err)
+{
+	struct mailbox_msg *respmsg;
+	struct mailbox_msg *reqmsg = (struct mailbox_msg *)arg;
+	struct mailbox *mbx = reqmsg->mbm_ch->mbc_parent;
+
+	/*
+	 * Can't send out request msg.
+	 * Removing corresponding response msg from queue and return error.
+	 */
+	if (err) {
+		respmsg = chan_msg_dequeue(&mbx->mbx_rx, reqmsg->mbm_req_id);
+		if (respmsg)
+			msg_done(respmsg, err);
+	}
+}
+
+static void dft_post_msg_cb(void *arg, void *buf, size_t len, u64 id, int err)
+{
+	struct mailbox_msg *msg = (struct mailbox_msg *)arg;
+
+	if (err) {
+		MBX_ERR(msg->mbm_ch->mbc_parent,
+			"failed to post msg, err=%d", err);
+	}
+}
+
+/*
+ * Msg will be sent to peer and reply will be received.
+ */
+int mailbox_request(struct platform_device *pdev, void *req, size_t reqlen,
+	void *resp, size_t *resplen, mailbox_msg_cb_t cb, void *cbarg)
+{
+	int rv = -ENOMEM;
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	struct mailbox_msg *reqmsg = NULL, *respmsg = NULL;
+
+	MBX_INFO(mbx, "sending request: %d", ((struct mailbox_req *)req)->req);
+
+	if (cb) {
+		reqmsg = alloc_msg(NULL, reqlen);
+		if (reqmsg)
+			(void) memcpy(reqmsg->mbm_data, req, reqlen);
+	} else {
+		reqmsg = alloc_msg(req, reqlen);
+	}
+	if (!reqmsg)
+		goto fail;
+	reqmsg->mbm_cb = dft_req_msg_cb;
+	reqmsg->mbm_cb_arg = reqmsg;
+	reqmsg->mbm_req_id = (uintptr_t)reqmsg->mbm_data;
+
+	respmsg = alloc_msg(resp, *resplen);
+	if (!respmsg)
+		goto fail;
+	respmsg->mbm_cb = cb;
+	respmsg->mbm_cb_arg = cbarg;
+	/* Only interested in response w/ same ID. */
+	respmsg->mbm_req_id = reqmsg->mbm_req_id;
+
+	/* Always enqueue RX msg before TX one to avoid race. */
+	rv = chan_msg_enqueue(&mbx->mbx_rx, respmsg);
+	if (rv)
+		goto fail;
+	rv = chan_msg_enqueue(&mbx->mbx_tx, reqmsg);
+	if (rv) {
+		respmsg = chan_msg_dequeue(&mbx->mbx_rx, reqmsg->mbm_req_id);
+		goto fail;
+	}
+
+	/* Kick TX channel to try to send out msg. */
+	complete(&mbx->mbx_tx.mbc_worker);
+
+	if (cb)
+		return 0;
+
+	wait_for_completion(&respmsg->mbm_complete);
+	rv = respmsg->mbm_error;
+	if (rv == 0)
+		*resplen = respmsg->mbm_len;
+
+	free_msg(respmsg);
+	return rv;
+
+fail:
+	if (reqmsg)
+		free_msg(reqmsg);
+	if (respmsg)
+		free_msg(respmsg);
+	return rv;
+}
+
+/*
+ * Msg will be posted, no wait for reply.
+ */
+int mailbox_post(struct platform_device *pdev, u64 reqid, void *buf, size_t len)
+{
+	int rv = 0;
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	struct mailbox_msg *msg = alloc_msg(NULL, len);
+
+	if (reqid == 0) {
+		MBX_DBG(mbx, "posting request: %d",
+			((struct mailbox_req *)buf)->req);
+	} else {
+		MBX_DBG(mbx, "posting response...");
+	}
+
+	if (!msg)
+		return -ENOMEM;
+
+	(void) memcpy(msg->mbm_data, buf, len);
+	msg->mbm_cb = dft_post_msg_cb;
+	msg->mbm_cb_arg = msg;
+	if (reqid) {
+		msg->mbm_req_id = reqid;
+		msg->mbm_flags |= MSG_FLAG_RESPONSE;
+	} else {
+		msg->mbm_req_id = (uintptr_t)msg->mbm_data;
+	}
+
+	rv = chan_msg_enqueue(&mbx->mbx_tx, msg);
+	if (rv)
+		free_msg(msg);
+
+	/* Kick TX channel to try to send out msg. */
+	complete(&mbx->mbx_tx.mbc_worker);
+
+	return rv;
+}
+/*
+ *   should not be called by other than connect_state_handler
+ */
+static int mailbox_connection_notify(struct platform_device *pdev, uint64_t sec_id, uint64_t flag)
+{
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	struct mailbox_req *mb_req = NULL;
+	struct mailbox_conn mb_conn = { 0 };
+	int  ret = 0;
+	size_t data_len = 0, reqlen = 0;
+	data_len = sizeof(struct mailbox_conn);
+	reqlen = sizeof(struct mailbox_req) + data_len;
+
+	mb_req = (struct mailbox_req *)vmalloc(reqlen);
+	if (!mb_req) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	mb_req->req = MAILBOX_REQ_CONN_EXPL;
+	if (!mbx->mbx_kaddr) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	mb_conn.kaddr = mbx->mbx_kaddr;
+	mb_conn.paddr = virt_to_phys(mbx->mbx_kaddr);
+	mb_conn.crc32 = crc32c_le(~0, mbx->mbx_kaddr, PAGE_SIZE);
+	mb_conn.flag = flag;
+	mb_conn.ver = mbx->mbx_prot_ver;
+
+	if (sec_id != 0) {
+		mb_conn.sec_id = sec_id;
+	} else {
+		mb_conn.sec_id = (uint64_t)mbx->mbx_kaddr;
+		mbx->mbx_conn_id = (uint64_t)mbx->mbx_kaddr;
+	}
+
+	memcpy(mb_req->data, &mb_conn, data_len);
+
+	ret = mailbox_post(pdev, 0, mb_req, reqlen);
+
+done:
+	vfree(mb_req);
+	return ret;
+}
+
+static int mailbox_connection_explore(struct platform_device *pdev, struct mailbox_conn *mb_conn)
+{
+	int ret = 0;
+	uint32_t crc_chk;
+	phys_addr_t paddr;
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	if (!mb_conn) {
+		ret = -EFAULT;
+		goto done;
+	}
+
+	paddr = virt_to_phys(mb_conn->kaddr);
+	if (paddr != mb_conn->paddr) {
+		MBX_INFO(mbx, "mb_conn->paddr %llx paddr: %llx\n", mb_conn->paddr, paddr);
+		MBX_INFO(mbx, "Failed to get the same physical addr, running in VMs?\n");
+		ret = -EFAULT;
+		goto done;
+	}
+	crc_chk = crc32c_le(~0, mb_conn->kaddr, PAGE_SIZE);
+
+	if (crc_chk != mb_conn->crc32) {
+		MBX_INFO(mbx, "crc32  : %x, %x\n",  mb_conn->crc32, crc_chk);
+		MBX_INFO(mbx, "failed to get the same CRC\n");
+		ret = -EFAULT;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int mailbox_get_data(struct platform_device *pdev, enum data_kind kind)
+{
+	int ret = 0;
+	switch (kind) {
+	case PEER_CONN:
+		ret = mailbox_connect_status(pdev);
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+
+static void connect_state_handler(struct mailbox *mbx, struct mailbox_conn *conn)
+{
+		int ret = 0;
+
+		if (!mbx || !conn)
+			return;
+
+		mutex_lock(&mbx->mbx_lock);
+
+		switch (conn->flag) {
+		case MB_CONN_INIT:
+			/* clean up all cached data, */
+			mbx->mbx_paired = 0;
+			mbx->mbx_established = false;
+			kfree(mbx->mbx_kaddr);
+
+			mbx->mbx_kaddr = kzalloc(PAGE_SIZE, GFP_KERNEL);
+			get_random_bytes(mbx->mbx_kaddr, PAGE_SIZE);
+			ret = mailbox_connection_notify(mbx->mbx_pdev, 0, MB_CONN_SYN);
+			if (ret)
+				goto done;
+			mbx->mbx_state = CONN_SYN_SENT;
+			break;
+		case MB_CONN_SYN:
+			if (mbx->mbx_state == CONN_SYN_SENT) {
+				if (!mailbox_connection_explore(mbx->mbx_pdev, conn)) {
+					mbx->mbx_paired |= 0x2;
+					MBX_INFO(mbx, "mailbox mbx_prot_ver %x", mbx->mbx_prot_ver);
+				}
+				ret = mailbox_connection_notify(mbx->mbx_pdev, conn->sec_id, MB_CONN_ACK);
+				if (ret)
+					goto done;
+				mbx->mbx_state = CONN_SYN_RECV;
+			} else
+				mbx->mbx_state = CONN_START;
+			break;
+		case MB_CONN_ACK:
+			if (mbx->mbx_state & (CONN_SYN_SENT | CONN_SYN_RECV)) {
+				if (mbx->mbx_conn_id == (uint64_t)conn->sec_id) {
+					mbx->mbx_paired |= 0x1;
+					mbx->mbx_established = true;
+					mbx->mbx_state = CONN_ESTABLISH;
+					kfree(mbx->mbx_kaddr);
+					mbx->mbx_kaddr = NULL;
+				} else
+					mbx->mbx_state = CONN_START;
+			}
+			break;
+		case MB_CONN_FIN:
+			mbx->mbx_paired = 0;
+			mbx->mbx_established = false;
+			kfree(mbx->mbx_kaddr);
+			mbx->mbx_kaddr = NULL;
+			mbx->mbx_state = CONN_START;
+			break;
+		default:
+			break;
+		}
+done:
+	if (ret) {
+		kfree(mbx->mbx_kaddr);
+		mbx->mbx_kaddr = NULL;
+		mbx->mbx_paired = 0;
+		mbx->mbx_state = CONN_START;
+	}
+	mutex_unlock(&mbx->mbx_lock);
+	MBX_INFO(mbx, "mailbox connection state %d", mbx->mbx_paired);
+}
+
+static void process_request(struct mailbox *mbx, struct mailbox_msg *msg)
+{
+	struct mailbox_req *req = (struct mailbox_req *)msg->mbm_data;
+	struct mailbox_conn *conn = (struct mailbox_conn *)req->data;
+	int rc;
+	const char *recvstr = "received request from peer";
+	const char *sendstr = "sending test msg to peer";
+
+	if (req->req == MAILBOX_REQ_TEST_READ) {
+		MBX_INFO(mbx, "%s: %d", recvstr, req->req);
+		if (mbx->mbx_tst_tx_msg_len) {
+			MBX_INFO(mbx, "%s", sendstr);
+			rc = mailbox_post(mbx->mbx_pdev, msg->mbm_req_id,
+				mbx->mbx_tst_tx_msg, mbx->mbx_tst_tx_msg_len);
+			if (rc)
+				MBX_ERR(mbx, "%s failed: %d", sendstr, rc);
+			else
+				mbx->mbx_tst_tx_msg_len = 0;
+
+		}
+	} else if (req->req == MAILBOX_REQ_TEST_READY) {
+		MBX_INFO(mbx, "%s: %d", recvstr, req->req);
+	} else if (req->req == MAILBOX_REQ_CONN_EXPL) {
+		MBX_INFO(mbx, "%s: %d", recvstr, req->req);
+		if (mbx->mbx_state != CONN_SYN_SENT) {
+			/* if your peer droped without notice,
+			 * initial the connection Simultaneously
+			 * again.
+			 */
+			if (conn->flag == MB_CONN_SYN) {
+				connect_state_touch(mbx, MB_CONN_INIT);
+			}
+		}
+		connect_state_handler(mbx, conn);
+	} else if (mbx->mbx_listen_cb) {
+		/* Call client's registered callback to process request. */
+		MBX_DBG(mbx, "%s: %d, passed on", recvstr, req->req);
+		mbx->mbx_listen_cb(mbx->mbx_listen_cb_arg, msg->mbm_data,
+			msg->mbm_len, msg->mbm_req_id, msg->mbm_error);
+	} else {
+		MBX_INFO(mbx, "%s: %d, dropped", recvstr, req->req);
+	}
+}
+
+/*
+ * Wait for request from peer.
+ */
+static void mailbox_recv_request(struct work_struct *work)
+{
+	int rv = 0;
+	struct mailbox_msg *msg = NULL;
+	struct mailbox *mbx =
+		container_of(work, struct mailbox, mbx_listen_worker);
+
+	for (;;) {
+		/* Only interested in request msg. */
+
+		rv = wait_for_completion_interruptible(&mbx->mbx_comp);
+		if (rv)
+			break;
+		mutex_lock(&mbx->mbx_lock);
+		msg = list_first_entry_or_null(&mbx->mbx_req_list,
+			struct mailbox_msg, mbm_list);
+
+		if (msg) {
+			list_del(&msg->mbm_list);
+			mbx->mbx_req_cnt--;
+			mbx->mbx_req_sz -= msg->mbm_len;
+			mutex_unlock(&mbx->mbx_lock);
+		} else {
+			mutex_unlock(&mbx->mbx_lock);
+			break;
+		}
+
+		process_request(mbx, msg);
+		free_msg(msg);
+	}
+
+	if (rv == -ESHUTDOWN)
+		MBX_INFO(mbx, "channel is closed, no listen to peer");
+	else if (rv != 0)
+		MBX_ERR(mbx, "failed to receive request from peer, err=%d", rv);
+
+	if (msg)
+		free_msg(msg);
+}
+
+int mailbox_listen(struct platform_device *pdev,
+	mailbox_msg_cb_t cb, void *cbarg)
+{
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+
+	mbx->mbx_listen_cb_arg = cbarg;
+	/* mbx->mbx_listen_cb is used in another thread as a condition to
+	 * call the function. Ensuring that the argument is captured before
+	 * the function pointer
+	 */
+	wmb();
+	mbx->mbx_listen_cb = cb;
+
+	return 0;
+}
+
+static int mailbox_enable_intr_mode(struct mailbox *mbx)
+{
+	struct resource *res;
+	int ret;
+	struct platform_device *pdev = mbx->mbx_pdev;
+	struct xocl_dev *xdev = xocl_get_xdev(pdev);
+
+	if (mbx->mbx_irq != -1)
+		return 0;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL) {
+		MBX_ERR(mbx, "failed to acquire intr resource");
+		return -EINVAL;
+	}
+
+	ret = xocl_user_interrupt_reg(xdev, res->start, mailbox_isr, mbx);
+	if (ret) {
+		MBX_ERR(mbx, "failed to add intr handler");
+		return ret;
+	}
+	ret = xocl_user_interrupt_config(xdev, res->start, true);
+	BUG_ON(ret != 0);
+
+	/* Only see intr when we have full packet sent or received. */
+	mailbox_reg_wr(mbx, &mbx->mbx_regs->mbr_rit, PACKET_SIZE - 1);
+	mailbox_reg_wr(mbx, &mbx->mbx_regs->mbr_sit, 0);
+
+	/* Finally, enable TX / RX intr. */
+	mailbox_reg_wr(mbx, &mbx->mbx_regs->mbr_ie, 0x3);
+
+	clear_bit(MBXCS_BIT_POLL_MODE, &mbx->mbx_rx.mbc_state);
+	chan_config_timer(&mbx->mbx_rx);
+
+	clear_bit(MBXCS_BIT_POLL_MODE, &mbx->mbx_tx.mbc_state);
+	chan_config_timer(&mbx->mbx_tx);
+
+	mbx->mbx_irq = res->start;
+	return 0;
+}
+
+static void mailbox_disable_intr_mode(struct mailbox *mbx)
+{
+	struct platform_device *pdev = mbx->mbx_pdev;
+	struct xocl_dev *xdev = xocl_get_xdev(pdev);
+
+	/*
+	 * No need to turn on polling mode for TX, which has
+	 * a channel stall checking timer always on when there is
+	 * outstanding TX packet.
+	 */
+	set_bit(MBXCS_BIT_POLL_MODE, &mbx->mbx_rx.mbc_state);
+	chan_config_timer(&mbx->mbx_rx);
+
+	/* Disable both TX / RX intrs. */
+	mailbox_reg_wr(mbx, &mbx->mbx_regs->mbr_ie, 0x0);
+
+	mailbox_reg_wr(mbx, &mbx->mbx_regs->mbr_rit, 0x0);
+	mailbox_reg_wr(mbx, &mbx->mbx_regs->mbr_sit, 0x0);
+
+	if (mbx->mbx_irq == -1)
+		return;
+
+	(void) xocl_user_interrupt_config(xdev, mbx->mbx_irq, false);
+	(void) xocl_user_interrupt_reg(xdev, mbx->mbx_irq, NULL, mbx);
+
+	mbx->mbx_irq = -1;
+}
+
+int mailbox_reset(struct platform_device *pdev, bool end_of_reset)
+{
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+	int ret = 0;
+
+	if (mailbox_no_intr)
+		return 0;
+
+	if (end_of_reset) {
+		MBX_INFO(mbx, "enable intr mode");
+		if (mailbox_enable_intr_mode(mbx) != 0)
+			MBX_ERR(mbx, "failed to enable intr after reset");
+	} else {
+		MBX_INFO(mbx, "enable polling mode");
+		mailbox_disable_intr_mode(mbx);
+	}
+	return ret;
+}
+
+/* Kernel APIs exported from this sub-device driver. */
+static struct xocl_mailbox_funcs mailbox_ops = {
+	.request = mailbox_request,
+	.post = mailbox_post,
+	.listen = mailbox_listen,
+	.reset = mailbox_reset,
+	.get_data = mailbox_get_data,
+};
+
+static int mailbox_remove(struct platform_device *pdev)
+{
+	struct mailbox *mbx = platform_get_drvdata(pdev);
+
+	BUG_ON(mbx == NULL);
+
+	connect_state_touch(mbx, MB_CONN_FIN);
+
+	mailbox_disable_intr_mode(mbx);
+
+	sysfs_remove_group(&pdev->dev.kobj, &mailbox_attrgroup);
+
+	chan_fini(&mbx->mbx_rx);
+	chan_fini(&mbx->mbx_tx);
+	listen_wq_fini(mbx);
+
+	BUG_ON(!(list_empty(&mbx->mbx_req_list)));
+
+	xocl_subdev_register(pdev, XOCL_SUBDEV_MAILBOX, NULL);
+
+	if (mbx->mbx_regs)
+		iounmap(mbx->mbx_regs);
+
+	MBX_INFO(mbx, "mailbox cleaned up successfully");
+	platform_set_drvdata(pdev, NULL);
+	kfree(mbx);
+	return 0;
+}
+
+static int mailbox_probe(struct platform_device *pdev)
+{
+	struct mailbox *mbx = NULL;
+	struct resource *res;
+	int ret;
+
+	mbx = kzalloc(sizeof(struct mailbox), GFP_KERNEL);
+	if (!mbx)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, mbx);
+	mbx->mbx_pdev = pdev;
+	mbx->mbx_irq = (u32)-1;
+
+
+	init_completion(&mbx->mbx_comp);
+	mutex_init(&mbx->mbx_lock);
+	INIT_LIST_HEAD(&mbx->mbx_req_list);
+	mbx->mbx_req_cnt = 0;
+	mbx->mbx_req_sz = 0;
+
+	mutex_init(&mbx->mbx_conn_lock);
+	mbx->mbx_established = false;
+	mbx->mbx_conn_id = 0;
+	mbx->mbx_kaddr = NULL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mbx->mbx_regs = ioremap_nocache(res->start, res->end - res->start + 1);
+	if (!mbx->mbx_regs) {
+		MBX_ERR(mbx, "failed to map in registers");
+		ret = -EIO;
+		goto failed;
+	}
+	/* Reset TX channel, RX channel is managed by peer as his TX. */
+	mailbox_reg_wr(mbx, &mbx->mbx_regs->mbr_ctrl, 0x1);
+
+	/* Set up software communication channels. */
+	ret = chan_init(mbx, "RX", &mbx->mbx_rx, chan_do_rx);
+	if (ret != 0) {
+		MBX_ERR(mbx, "failed to init rx channel");
+		goto failed;
+	}
+	ret = chan_init(mbx, "TX", &mbx->mbx_tx, chan_do_tx);
+	if (ret != 0) {
+		MBX_ERR(mbx, "failed to init tx channel");
+		goto failed;
+	}
+	/* Dedicated thread for listening to peer request. */
+	mbx->mbx_listen_wq =
+		create_singlethread_workqueue(dev_name(&mbx->mbx_pdev->dev));
+	if (!mbx->mbx_listen_wq) {
+		MBX_ERR(mbx, "failed to create request-listen work queue");
+		goto failed;
+	}
+	INIT_WORK(&mbx->mbx_listen_worker, mailbox_recv_request);
+	queue_work(mbx->mbx_listen_wq, &mbx->mbx_listen_worker);
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &mailbox_attrgroup);
+	if (ret != 0) {
+		MBX_ERR(mbx, "failed to init sysfs");
+		goto failed;
+	}
+
+	if (mailbox_no_intr) {
+		MBX_INFO(mbx, "Enabled timer-driven mode");
+		mailbox_disable_intr_mode(mbx);
+	} else {
+		ret = mailbox_enable_intr_mode(mbx);
+		if (ret != 0)
+			goto failed;
+	}
+
+	xocl_subdev_register(pdev, XOCL_SUBDEV_MAILBOX, &mailbox_ops);
+
+	connect_state_touch(mbx, MB_CONN_INIT);
+	mbx->mbx_prot_ver = MB_PROTOCOL_VER;
+
+	MBX_INFO(mbx, "successfully initialized");
+	return 0;
+
+failed:
+	mailbox_remove(pdev);
+	return ret;
+}
+
+struct platform_device_id mailbox_id_table[] = {
+	{ XOCL_MAILBOX, 0 },
+	{ },
+};
+
+static struct platform_driver mailbox_driver = {
+	.probe		= mailbox_probe,
+	.remove		= mailbox_remove,
+	.driver		= {
+		.name	= XOCL_MAILBOX,
+	},
+	.id_table = mailbox_id_table,
+};
+
+int __init xocl_init_mailbox(void)
+{
+	BUILD_BUG_ON(sizeof(struct mailbox_pkt) != sizeof(u32) * PACKET_SIZE);
+	return platform_driver_register(&mailbox_driver);
+}
+
+void xocl_fini_mailbox(void)
+{
+	platform_driver_unregister(&mailbox_driver);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/mb_scheduler.c b/drivers/gpu/drm/xocl/subdev/mb_scheduler.c
new file mode 100644
index 000000000000..b3ed3ae0b41a
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/mb_scheduler.c
@@ -0,0 +1,3059 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2018-2019 Xilinx, Inc. All rights reserved.
+ *
+ * Authors:
+ *    Soren Soe <soren.soe@xilinx.com>
+ */
+
+/*
+ * Kernel Driver Scheduler (KDS) for XRT
+ *
+ * struct xocl_cmd
+ *  - wraps exec BOs create from user space
+ *  - transitions through a number of states
+ *  - initially added to pending command queue
+ *  - consumed by scheduler which manages its execution (state transition)
+ * struct xcol_cu
+ *  - compute unit for executing commands
+ *  - used only without embedded scheduler (ert)
+ *  - talks to HW compute units
+ * struct xocl_ert
+ *  - embedded scheduler for executing commands on ert
+ *  - talks to HW ERT
+ * struct exec_core
+ *  - execution core managing execution on one device
+ * struct xocl_scheduler
+ *  - manages execution of cmds on one or more exec cores
+ *  - executed in a separate kernel thread
+ *  - loops repeatedly when there is work to do
+ *  - moves pending commands into a scheduler command queue
+ *
+ * [new -> pending]. The xocl API adds exec BOs to KDS.	 The exec BOs are
+ * wrapped in a xocl_cmd object and added to a pending command queue.
+ *
+ * [pending -> queued]. Scheduler loops repeatedly and copies pending commands
+ * to its own command queue, then managaes command execution on one or more
+ * execution cores.
+ *
+ * [queued -> submitted]. Commands are submitted for execution on execution
+ * core when the core has room for new commands.
+ *
+ * [submitted -> running]. Once submitted, a command is transition by
+ * scheduler into running state when there is an available compute unit (no
+ * ert) or if ERT is used, then when ERT has room.
+ *
+ * [running -> complete]. Commands running on ERT complete by sending an
+ * interrupt to scheduler.  When ERT is not used, commands are running on a
+ * compute unit and are polled for completion.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/list.h>
+#include <linux/eventfd.h>
+#include <linux/kthread.h>
+#include "../ert.h"
+#include "../xocl_drv.h"
+#include "../userpf/common.h"
+
+//#define SCHED_VERBOSE
+
+#if defined(__GNUC__)
+#define SCHED_UNUSED __attribute__((unused))
+#endif
+
+#define sched_debug_packet(packet, size)				\
+({									\
+	int i;								\
+	u32 *data = (u32 *)packet;					\
+	for (i = 0; i < size; ++i)					    \
+		DRM_INFO("packet(0x%p) data[%d] = 0x%x\n", data, i, data[i]); \
+})
+
+#ifdef SCHED_VERBOSE
+# define SCHED_DEBUG(msg) DRM_INFO(msg)
+# define SCHED_DEBUGF(format, ...) DRM_INFO(format, ##__VA_ARGS__)
+# define SCHED_PRINTF(format, ...) DRM_INFO(format, ##__VA_ARGS__)
+# define SCHED_DEBUG_PACKET(packet, size) sched_debug_packet(packet, size)
+#else
+# define SCHED_DEBUG(msg)
+# define SCHED_DEBUGF(format, ...)
+# define SCHED_PRINTF(format, ...) DRM_INFO(format, ##__VA_ARGS__)
+# define SCHED_DEBUG_PACKET(packet, size)
+#endif
+
+/* constants */
+static const unsigned int no_index = -1;
+
+/* FFA	handling */
+static const u32 AP_START    = 0x1;
+static const u32 AP_DONE     = 0x2;
+static const u32 AP_IDLE     = 0x4;
+static const u32 AP_READY    = 0x8;
+static const u32 AP_CONTINUE = 0x10;
+
+/* Forward declaration */
+struct exec_core;
+struct exec_ops;
+struct xocl_scheduler;
+
+static int validate(struct platform_device *pdev, struct client_ctx *client,
+		    const struct drm_xocl_bo *bo);
+static bool exec_is_flush(struct exec_core *exec);
+static void scheduler_wake_up(struct xocl_scheduler *xs);
+static void scheduler_intr(struct xocl_scheduler *xs);
+static void scheduler_decr_poll(struct xocl_scheduler *xs);
+
+/*
+ */
+static void
+xocl_bitmap_to_arr32(u32 *buf, const unsigned long *bitmap, unsigned int nbits)
+{
+	unsigned int i, halfwords;
+
+	halfwords = DIV_ROUND_UP(nbits, 32);
+	for (i = 0; i < halfwords; i++) {
+		buf[i] = (u32) (bitmap[i/2] & UINT_MAX);
+		if (++i < halfwords)
+			buf[i] = (u32) (bitmap[i/2] >> 32);
+	}
+
+	/* Clear tail bits in last element of array beyond nbits. */
+	if (nbits % BITS_PER_LONG)
+		buf[halfwords - 1] &= (u32) (UINT_MAX >> ((-nbits) & 31));
+}
+
+static void
+xocl_bitmap_from_arr32(unsigned long *bitmap, const u32 *buf, unsigned int nbits)
+{
+	unsigned int i, halfwords;
+
+	halfwords = DIV_ROUND_UP(nbits, 32);
+	for (i = 0; i < halfwords; i++) {
+		bitmap[i/2] = (unsigned long) buf[i];
+		if (++i < halfwords)
+			bitmap[i/2] |= ((unsigned long) buf[i]) << 32;
+	}
+
+	/* Clear tail bits in last word beyond nbits. */
+	if (nbits % BITS_PER_LONG)
+		bitmap[(halfwords - 1) / 2] &= BITMAP_LAST_WORD_MASK(nbits);
+}
+
+
+/**
+ * slot_mask_idx() - Slot mask idx index for a given slot_idx
+ *
+ * @slot_idx: Global [0..127] index of a CQ slot
+ * Return: Index of the slot mask containing the slot_idx
+ */
+static inline unsigned int
+slot_mask_idx(unsigned int slot_idx)
+{
+	return slot_idx >> 5;
+}
+
+/**
+ * slot_idx_in_mask() - Index of command queue slot within the mask that contains it
+ *
+ * @slot_idx: Global [0..127] index of a CQ slot
+ * Return: Index of slot within the mask that contains it
+ */
+static inline unsigned int
+slot_idx_in_mask(unsigned int slot_idx)
+{
+	return slot_idx - (slot_mask_idx(slot_idx) << 5);
+}
+
+/**
+ * Command data used by scheduler
+ *
+ * @list: command object moves from pending to commmand queue list
+ * @cu_list: command object is added to CU list when running (penguin only)
+ *
+ * @bo: underlying drm buffer object
+ * @exec: execution device associated with this command
+ * @client: client (user process) context that created this command
+ * @xs: command scheduler responsible for schedulint this command
+ * @state: state of command object per scheduling
+ * @id: unique id for an active command object
+ * @cu_idx: index of CU executing this cmd object; used in penguin mode only
+ * @slot_idx: command queue index of this command object
+ * @wait_count: number of commands that must trigger this command before it can start
+ * @chain_count: number of commands that this command must trigger when it completes
+ * @chain: list of commands to trigger upon completion; maximum chain depth is 8
+ * @deps: list of commands this object depends on, converted to chain when command is queued
+ * @packet: mapped ert packet object from user space
+ */
+struct xocl_cmd {
+	struct list_head cq_list; // scheduler command queue
+	struct list_head rq_list; // exec core running queue
+
+	/* command packet */
+	struct drm_xocl_bo *bo;
+	union {
+		struct ert_packet	    *ecmd;
+		struct ert_start_kernel_cmd *kcmd;
+	};
+
+	DECLARE_BITMAP(cu_bitmap, MAX_CUS);
+
+	struct xocl_dev	  *xdev;
+	struct exec_core  *exec;
+	struct client_ctx *client;
+	struct xocl_scheduler *xs;
+	enum ert_cmd_state state;
+
+	/* dependency handling */
+	unsigned int chain_count;
+	unsigned int wait_count;
+	union {
+		struct xocl_cmd *chain[8];
+		struct drm_xocl_bo *deps[8];
+	};
+
+	unsigned long uid;     // unique id for this command
+	unsigned int cu_idx;   // index of CU running this cmd (penguin mode)
+	unsigned int slot_idx; // index in exec core submit queue
+};
+
+/*
+ * List of free xocl_cmd objects.
+ *
+ * @free_cmds: populated with recycled xocl_cmd objects
+ * @cmd_mutex: mutex lock for cmd_list
+ *
+ * Command objects are recycled for later use and only freed when kernel
+ * module is unloaded.
+ */
+static LIST_HEAD(free_cmds);
+static DEFINE_MUTEX(free_cmds_mutex);
+
+/**
+ * delete_cmd_list() - reclaim memory for all allocated command objects
+ */
+static void
+cmd_list_delete(void)
+{
+	struct xocl_cmd *xcmd;
+	struct list_head *pos, *next;
+
+	mutex_lock(&free_cmds_mutex);
+	list_for_each_safe(pos, next, &free_cmds) {
+		xcmd = list_entry(pos, struct xocl_cmd, cq_list);
+		list_del(pos);
+		kfree(xcmd);
+	}
+	mutex_unlock(&free_cmds_mutex);
+}
+
+/*
+ * opcode() - Command opcode
+ *
+ * @cmd: Command object
+ * Return: Opcode per command packet
+ */
+static inline u32
+cmd_opcode(struct xocl_cmd *xcmd)
+{
+	return xcmd->ecmd->opcode;
+}
+
+/*
+ * type() - Command type
+ *
+ * @cmd: Command object
+ * Return: Type of command
+ */
+static inline u32
+cmd_type(struct xocl_cmd *xcmd)
+{
+	return xcmd->ecmd->type;
+}
+
+/*
+ * exec() - Get execution core
+ */
+static inline struct exec_core *
+cmd_exec(struct xocl_cmd *xcmd)
+{
+	return xcmd->exec;
+}
+
+/*
+ * uid() - Get unique id of command
+ */
+static inline unsigned long
+cmd_uid(struct xocl_cmd *xcmd)
+{
+	return xcmd->uid;
+}
+
+/*
+ */
+static inline unsigned int
+cmd_wait_count(struct xocl_cmd *xcmd)
+{
+	return xcmd->wait_count;
+}
+
+/**
+ * payload_size() - Command payload size
+ *
+ * @xcmd: Command object
+ * Return: Size in number of words of command packet payload
+ */
+static inline unsigned int
+cmd_payload_size(struct xocl_cmd *xcmd)
+{
+	return xcmd->ecmd->count;
+}
+
+/**
+ * cmd_packet_size() - Command packet size
+ *
+ * @xcmd: Command object
+ * Return: Size in number of words of command packet
+ */
+static inline unsigned int
+cmd_packet_size(struct xocl_cmd *xcmd)
+{
+	return cmd_payload_size(xcmd) + 1;
+}
+
+/**
+ * cu_masks() - Number of command packet cu_masks
+ *
+ * @xcmd: Command object
+ * Return: Total number of CU masks in command packet
+ */
+static inline unsigned int
+cmd_cumasks(struct xocl_cmd *xcmd)
+{
+	return 1 + xcmd->kcmd->extra_cu_masks;
+}
+
+/**
+ * regmap_size() - Size of regmap is payload size (n) minus the number of cu_masks
+ *
+ * @xcmd: Command object
+ * Return: Size of register map in number of words
+ */
+static inline unsigned int
+cmd_regmap_size(struct xocl_cmd *xcmd)
+{
+	return cmd_payload_size(xcmd) - cmd_cumasks(xcmd);
+}
+
+/*
+ */
+static inline struct ert_packet*
+cmd_packet(struct xocl_cmd *xcmd)
+{
+	return xcmd->ecmd;
+}
+
+/*
+ */
+static inline u32*
+cmd_regmap(struct xocl_cmd *xcmd)
+{
+	return xcmd->kcmd->data + xcmd->kcmd->extra_cu_masks;
+}
+
+/**
+ * cmd_set_int_state() - Set internal command state used by scheduler only
+ *
+ * @xcmd: command to change internal state on
+ * @state: new command state per ert.h
+ */
+static inline void
+cmd_set_int_state(struct xocl_cmd *xcmd, enum ert_cmd_state state)
+{
+	SCHED_DEBUGF("-> %s(%lu,%d)\n", __func__, xcmd->uid, state);
+	xcmd->state = state;
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
+
+/**
+ * cmd_set_state() - Set both internal and external state of a command
+ *
+ * The state is reflected externally through the command packet
+ * as well as being captured in internal state variable
+ *
+ * @xcmd: command object
+ * @state: new state
+ */
+static inline void
+cmd_set_state(struct xocl_cmd *xcmd, enum ert_cmd_state state)
+{
+	SCHED_DEBUGF("->%s(%lu,%d)\n", __func__, xcmd->uid, state);
+	xcmd->state = state;
+	xcmd->ecmd->state = state;
+	SCHED_DEBUGF("<-%s\n", __func__);
+}
+
+/*
+ * update_state() - Update command state if client has aborted
+ */
+static enum ert_cmd_state
+cmd_update_state(struct xocl_cmd *xcmd)
+{
+	if (xcmd->state != ERT_CMD_STATE_RUNNING && xcmd->client->abort) {
+		userpf_info(xcmd->xdev, "aborting stale client cmd(%lu)", xcmd->uid);
+		cmd_set_state(xcmd, ERT_CMD_STATE_ABORT);
+	}
+	if (exec_is_flush(xcmd->exec)) {
+		userpf_info(xcmd->xdev, "aborting stale exec cmd(%lu)", xcmd->uid);
+		cmd_set_state(xcmd, ERT_CMD_STATE_ABORT);
+	}
+	return xcmd->state;
+}
+
+/*
+ * release_gem_object_reference() -
+ */
+static inline void
+cmd_release_gem_object_reference(struct xocl_cmd *xcmd)
+{
+	if (xcmd->bo)
+		drm_gem_object_put_unlocked(&xcmd->bo->base);
+//PORT4_20
+//		drm_gem_object_unreference_unlocked(&xcmd->bo->base);
+}
+
+/*
+ */
+static inline void
+cmd_mark_active(struct xocl_cmd *xcmd)
+{
+	if (xcmd->bo)
+		xcmd->bo->metadata.active = xcmd;
+}
+
+/*
+ */
+static inline void
+cmd_mark_deactive(struct xocl_cmd *xcmd)
+{
+	if (xcmd->bo)
+		xcmd->bo->metadata.active = NULL;
+}
+
+/**
+ * chain_dependencies() - Chain this command to its dependencies
+ *
+ * @xcmd: Command to chain to its dependencies
+ *
+ * This function looks at all incoming explicit BO dependencies, checks if a
+ * corresponding xocl_cmd object exists (is active) in which case that command
+ * object must chain argument xcmd so that it (xcmd) can be triggered when
+ * dependency completes.  The chained command has a wait count corresponding to
+ * the number of dependencies that are active.
+ */
+static int
+cmd_chain_dependencies(struct xocl_cmd *xcmd)
+{
+	int didx;
+	int dcount = xcmd->wait_count;
+
+	SCHED_DEBUGF("-> chain_dependencies of xcmd(%lu)\n", xcmd->uid);
+	for (didx = 0; didx < dcount; ++didx) {
+		struct drm_xocl_bo *dbo = xcmd->deps[didx];
+		struct xocl_cmd *chain_to = dbo->metadata.active;
+		// release reference created in ioctl call when dependency was looked up
+		// see comments in xocl_ioctl.c:xocl_execbuf_ioctl()
+//PORT4_20
+//		drm_gem_object_unreference_unlocked(&dbo->base);
+		drm_gem_object_put_unlocked(&dbo->base);
+		xcmd->deps[didx] = NULL;
+		if (!chain_to) { /* command may have completed already */
+			--xcmd->wait_count;
+			continue;
+		}
+		if (chain_to->chain_count >= MAX_DEPS) {
+			DRM_INFO("chain count exceeded");
+			return 1;
+		}
+		SCHED_DEBUGF("+ xcmd(%lu)->chain[%d]=xcmd(%lu)", chain_to->uid, chain_to->chain_count, xcmd->uid);
+		chain_to->chain[chain_to->chain_count++] = xcmd;
+	}
+	SCHED_DEBUG("<- chain_dependencies\n");
+	return 0;
+}
+
+/**
+ * trigger_chain() - Trigger the execution of any commands chained to argument command
+ *
+ * @xcmd: Completed command that must trigger its chained (waiting) commands
+ *
+ * The argument command has completed and must trigger the execution of all
+ * chained commands whos wait_count is 0.
+ */
+static void
+cmd_trigger_chain(struct xocl_cmd *xcmd)
+{
+	SCHED_DEBUGF("-> trigger_chain xcmd(%lu)\n", xcmd->uid);
+	while (xcmd->chain_count) {
+		struct xocl_cmd *trigger = xcmd->chain[--xcmd->chain_count];
+
+		SCHED_DEBUGF("+ cmd(%lu) triggers cmd(%lu) with wait_count(%d)\n",
+			     xcmd->uid, trigger->uid, trigger->wait_count);
+		// decrement trigger wait count
+		// scheduler will submit when wait count reaches zero
+		--trigger->wait_count;
+	}
+	SCHED_DEBUG("<- trigger_chain\n");
+}
+
+
+/**
+ * cmd_get() - Get a free command object
+ *
+ * Get from free/recycled list or allocate a new command if necessary.
+ *
+ * Return: Free command object
+ */
+static struct xocl_cmd*
+cmd_get(struct xocl_scheduler *xs, struct exec_core *exec, struct client_ctx *client)
+{
+	struct xocl_cmd *xcmd;
+	static unsigned long count;
+
+	mutex_lock(&free_cmds_mutex);
+	xcmd = list_first_entry_or_null(&free_cmds, struct xocl_cmd, cq_list);
+	if (xcmd)
+		list_del(&xcmd->cq_list);
+	mutex_unlock(&free_cmds_mutex);
+	if (!xcmd)
+		xcmd = kmalloc(sizeof(struct xocl_cmd), GFP_KERNEL);
+	if (!xcmd)
+		return ERR_PTR(-ENOMEM);
+	xcmd->uid = count++;
+	xcmd->exec = exec;
+	xcmd->cu_idx = no_index;
+	xcmd->slot_idx = no_index;
+	xcmd->xs = xs;
+	xcmd->xdev = client->xdev;
+	xcmd->client = client;
+	xcmd->bo = NULL;
+	xcmd->ecmd = NULL;
+	atomic_inc(&client->outstanding_execs);
+	SCHED_DEBUGF("xcmd(%lu) xcmd(%p) [-> new ]\n", xcmd->uid, xcmd);
+	return xcmd;
+}
+
+/**
+ * cmd_free() - free a command object
+ *
+ * @xcmd: command object to free (move to freelist)
+ *
+ * The command *is* in some current list (scheduler command queue)
+ */
+static void
+cmd_free(struct xocl_cmd *xcmd)
+{
+	cmd_release_gem_object_reference(xcmd);
+
+	mutex_lock(&free_cmds_mutex);
+	list_move_tail(&xcmd->cq_list, &free_cmds);
+	mutex_unlock(&free_cmds_mutex);
+
+	atomic_dec(&xcmd->xdev->outstanding_execs);
+	atomic_dec(&xcmd->client->outstanding_execs);
+	SCHED_DEBUGF("xcmd(%lu) [-> free]\n", xcmd->uid);
+}
+
+/**
+ * abort_cmd() - abort command object before it becomes pending
+ *
+ * @xcmd: command object to abort (move to freelist)
+ *
+ * Command object is *not* in any current list
+ *
+ * Return: 0
+ */
+static void
+cmd_abort(struct xocl_cmd *xcmd)
+{
+	mutex_lock(&free_cmds_mutex);
+	list_add_tail(&xcmd->cq_list, &free_cmds);
+	mutex_unlock(&free_cmds_mutex);
+	SCHED_DEBUGF("xcmd(%lu) [-> abort]\n", xcmd->uid);
+}
+
+/*
+ * cmd_bo_init() - Initialize a command object with an exec BO
+ *
+ * In penguin mode, the command object caches the CUs available
+ * to execute the command.  When ERT is enabled, the CU info
+ * is not used.
+ */
+static void
+cmd_bo_init(struct xocl_cmd *xcmd, struct drm_xocl_bo *bo,
+	    int numdeps, struct drm_xocl_bo **deps, int penguin)
+{
+	SCHED_DEBUGF("%s(%lu,bo,%d,deps,%d)\n", __func__, xcmd->uid, numdeps, penguin);
+	xcmd->bo = bo;
+	xcmd->ecmd = (struct ert_packet *)bo->vmapping;
+
+	if (penguin && cmd_opcode(xcmd) == ERT_START_KERNEL) {
+		unsigned int i = 0;
+		u32 cumasks[4] = {0};
+
+		cumasks[0] = xcmd->kcmd->cu_mask;
+		SCHED_DEBUGF("+ xcmd(%lu) cumask[0]=0x%x\n", xcmd->uid, cumasks[0]);
+		for (i = 0; i < xcmd->kcmd->extra_cu_masks; ++i) {
+			cumasks[i+1] = xcmd->kcmd->data[i];
+			SCHED_DEBUGF("+ xcmd(%lu) cumask[%d]=0x%x\n", xcmd->uid, i+1, cumasks[i+1]);
+		}
+		xocl_bitmap_from_arr32(xcmd->cu_bitmap, cumasks, MAX_CUS);
+		SCHED_DEBUGF("cu_bitmap[0] = %lu\n", xcmd->cu_bitmap[0]);
+	}
+
+	// dependencies are copied here, the anticipated wait_count is number
+	// of specified dependencies.  The wait_count is adjusted when the
+	// command is queued in the scheduler based on whether or not a
+	// dependency is active (managed by scheduler)
+	memcpy(xcmd->deps, deps, numdeps*sizeof(struct drm_xocl_bo *));
+	xcmd->wait_count = numdeps;
+	xcmd->chain_count = 0;
+}
+
+/*
+ */
+static void
+cmd_packet_init(struct xocl_cmd *xcmd, struct ert_packet *packet)
+{
+	SCHED_DEBUGF("%s(%lu,packet)\n", __func__, xcmd->uid);
+	xcmd->ecmd = packet;
+}
+
+/*
+ * cmd_has_cu() - Check if this command object can execute on CU
+ *
+ * @cuidx: the index of the CU.	 Note that CU indicies start from 0.
+ */
+static int
+cmd_has_cu(struct xocl_cmd *xcmd, unsigned int cuidx)
+{
+	SCHED_DEBUGF("%s(%lu,%d) = %d\n", __func__, xcmd->uid, cuidx, test_bit(cuidx, xcmd->cu_bitmap));
+	return test_bit(cuidx, xcmd->cu_bitmap);
+}
+
+/*
+ * struct xocl_cu: Represents a compute unit in penguin mode
+ *
+ * @running_queue: a fifo representing commands running on this CU
+ * @xdev: the xrt device with this CU
+ * @idx: index of this CU
+ * @base: exec base address of this CU
+ * @addr: base address of this CU
+ * @ctrlreg: state of the CU (value of AXI-lite control register)
+ * @done_cnt: number of command that have completed (<=running_queue.size())
+ *
+ */
+struct xocl_cu {
+	struct list_head   running_queue;
+	unsigned int idx;
+	void __iomem *base;
+	u32 addr;
+
+	u32 ctrlreg;
+	unsigned int done_cnt;
+	unsigned int run_cnt;
+	unsigned int uid;
+};
+
+/*
+ */
+void
+cu_reset(struct xocl_cu *xcu, unsigned int idx, void __iomem *base, u32 addr)
+{
+	xcu->idx = idx;
+	xcu->base = base;
+	xcu->addr = addr;
+	xcu->ctrlreg = 0;
+	xcu->done_cnt = 0;
+	xcu->run_cnt = 0;
+	SCHED_DEBUGF("%s(uid:%d,idx:%d) @ 0x%x\n", __func__, xcu->uid, xcu->idx, xcu->addr);
+}
+
+/*
+ */
+struct xocl_cu *
+cu_create(void)
+{
+	struct xocl_cu *xcu = kmalloc(sizeof(struct xocl_cu), GFP_KERNEL);
+	static unsigned int uid;
+
+	INIT_LIST_HEAD(&xcu->running_queue);
+	xcu->uid = uid++;
+	SCHED_DEBUGF("%s(uid:%d)\n", __func__, xcu->uid);
+	return xcu;
+}
+
+static inline u32
+cu_base_addr(struct xocl_cu *xcu)
+{
+	return xcu->addr;
+}
+
+/*
+ */
+void
+cu_destroy(struct xocl_cu *xcu)
+{
+	SCHED_DEBUGF("%s(uid:%d)\n", __func__, xcu->uid);
+	kfree(xcu);
+}
+
+/*
+ */
+void
+cu_poll(struct xocl_cu *xcu)
+{
+	// assert !list_empty(&running_queue)
+	xcu->ctrlreg = ioread32(xcu->base + xcu->addr);
+	SCHED_DEBUGF("%s(%d) 0x%x done(%d) run(%d)\n", __func__, xcu->idx, xcu->ctrlreg, xcu->done_cnt, xcu->run_cnt);
+	if (xcu->ctrlreg & AP_DONE) {
+		++xcu->done_cnt; // assert done_cnt <= |running_queue|
+		--xcu->run_cnt;
+		// acknowledge done
+		iowrite32(AP_CONTINUE, xcu->base + xcu->addr);
+	}
+}
+
+/*
+ * cu_ready() - Check if CU is ready to start another command
+ *
+ * The CU is ready when AP_START is low
+ */
+static int
+cu_ready(struct xocl_cu *xcu)
+{
+	if (xcu->ctrlreg & AP_START)
+		cu_poll(xcu);
+
+	SCHED_DEBUGF("%s(%d) returns %d\n", __func__, xcu->idx, !(xcu->ctrlreg & AP_START));
+	return !(xcu->ctrlreg & AP_START);
+}
+
+/*
+ * cu_first_done() - Get the first completed command from the running queue
+ *
+ * Return: The first command that has completed or nullptr if none
+ */
+static struct xocl_cmd*
+cu_first_done(struct xocl_cu *xcu)
+{
+	if (!xcu->done_cnt)
+		cu_poll(xcu);
+
+	SCHED_DEBUGF("%s(%d) has done_cnt %d\n", __func__, xcu->idx, xcu->done_cnt);
+
+	return xcu->done_cnt
+		? list_first_entry(&xcu->running_queue, struct xocl_cmd, rq_list)
+		: NULL;
+}
+
+/*
+ * cu_pop_done() - Remove first element from running queue
+ */
+static void
+cu_pop_done(struct xocl_cu *xcu)
+{
+	struct xocl_cmd *xcmd;
+
+	if (!xcu->done_cnt)
+		return;
+	xcmd = list_first_entry(&xcu->running_queue, struct xocl_cmd, rq_list);
+	list_del(&xcmd->rq_list);
+	--xcu->done_cnt;
+	SCHED_DEBUGF("%s(%d) xcmd(%lu) done(%d) run(%d)\n", __func__, xcu->idx, xcmd->uid, xcu->done_cnt, xcu->run_cnt);
+}
+
+/*
+ * cu_start() - Start the CU with a new command.
+ *
+ * The command is pushed onto the running queue
+ */
+static int
+cu_start(struct xocl_cu *xcu, struct xocl_cmd *xcmd)
+{
+	// assert(!(ctrlreg & AP_START), "cu not ready");
+
+	// data past header and cu_masks
+	unsigned int size = cmd_regmap_size(xcmd);
+	u32 *regmap = cmd_regmap(xcmd);
+	unsigned int i;
+
+	// past header, past cumasks
+	SCHED_DEBUG_PACKET(regmap, size);
+
+	// write register map, starting at base + 0xC
+	// 0x4, 0x8 used for interrupt, which is initialized in setu
+	for (i = 1; i < size; ++i)
+		iowrite32(*(regmap + i), xcu->base + xcu->addr + (i << 2));
+
+	// start cu.  update local state as we may not be polling prior
+	// to next ready check.
+	xcu->ctrlreg |= AP_START;
+	iowrite32(AP_START, xcu->base + xcu->addr);
+
+	// add cmd to end of running queue
+	list_add_tail(&xcmd->rq_list, &xcu->running_queue);
+	++xcu->run_cnt;
+
+	SCHED_DEBUGF("%s(%d) started xcmd(%lu) done(%d) run(%d)\n",
+		     __func__, xcu->idx, xcmd->uid, xcu->done_cnt, xcu->run_cnt);
+
+	return true;
+}
+
+
+/*
+ * sruct xocl_ert: Represents embedded scheduler in ert mode
+ */
+struct xocl_ert {
+	void __iomem *base;
+	u32	     cq_addr;
+	unsigned int uid;
+
+	unsigned int slot_size;
+	unsigned int cq_intr;
+};
+
+/*
+ */
+struct xocl_ert *
+ert_create(void __iomem *base, u32 cq_addr)
+{
+	struct xocl_ert *xert = kmalloc(sizeof(struct xocl_ert), GFP_KERNEL);
+	static unsigned int uid;
+
+	xert->base = base;
+	xert->cq_addr = cq_addr;
+	xert->uid = uid++;
+	xert->slot_size = 0;
+	xert->cq_intr = false;
+	SCHED_DEBUGF("%s(%d,0x%x)\n", __func__, xert->uid, xert->cq_addr);
+	return xert;
+}
+
+/*
+ */
+static void
+ert_destroy(struct xocl_ert *xert)
+{
+	SCHED_DEBUGF("%s(%d)\n", __func__, xert->uid);
+	kfree(xert);
+}
+
+/*
+ */
+static void
+ert_cfg(struct xocl_ert *xert, unsigned int slot_size, unsigned int cq_intr)
+{
+	SCHED_DEBUGF("%s(%d) slot_size(%d) cq_intr(%d)\n", __func__, xert->uid, slot_size, cq_intr);
+	xert->slot_size = slot_size;
+	xert->cq_intr = cq_intr;
+}
+
+/*
+ */
+static bool
+ert_start_cmd(struct xocl_ert *xert, struct xocl_cmd *xcmd)
+{
+	u32 slot_addr = xert->cq_addr + xcmd->slot_idx * xert->slot_size;
+	struct ert_packet *ecmd = cmd_packet(xcmd);
+
+	SCHED_DEBUG_PACKET(ecmd, cmd_packet_size(xcmd));
+
+	SCHED_DEBUGF("-> %s(%d,%lu)\n", __func__, xert->uid, xcmd->uid);
+
+	// write packet minus header
+	SCHED_DEBUGF("++ slot_idx=%d, slot_addr=0x%x\n", xcmd->slot_idx, slot_addr);
+	memcpy_toio(xert->base + slot_addr + 4, ecmd->data, (cmd_packet_size(xcmd) - 1) * sizeof(u32));
+
+	// write header
+	iowrite32(ecmd->header, xert->base + slot_addr);
+
+	// trigger interrupt to embedded scheduler if feature is enabled
+	if (xert->cq_intr) {
+		u32 cq_int_addr = ERT_CQ_STATUS_REGISTER_ADDR + (slot_mask_idx(xcmd->slot_idx) << 2);
+		u32 mask = 1 << slot_idx_in_mask(xcmd->slot_idx);
+
+		SCHED_DEBUGF("++ mb_submit writes slot mask 0x%x to CQ_INT register at addr 0x%x\n",
+			     mask, cq_int_addr);
+		iowrite32(mask, xert->base + cq_int_addr);
+	}
+	SCHED_DEBUGF("<- %s returns true\n", __func__);
+	return true;
+}
+
+/*
+ */
+static void
+ert_read_custat(struct xocl_ert *xert, unsigned int num_cus, u32 *cu_usage, struct xocl_cmd *xcmd)
+{
+	u32 slot_addr = xert->cq_addr + xcmd->slot_idx*xert->slot_size;
+
+	memcpy_fromio(cu_usage, xert->base + slot_addr + 4, num_cus * sizeof(u32));
+}
+
+/**
+ * struct exec_ops: scheduler specific operations
+ *
+ * Scheduler can operate in MicroBlaze mode (mb/ert) or in penguin mode. This
+ * struct differentiates specific operations.  The struct is per device node,
+ * meaning that one device can operate in ert mode while another can operate
+ * in penguin mode.
+ */
+struct exec_ops {
+	bool (*start)(struct exec_core *exec, struct xocl_cmd *xcmd);
+	void (*query)(struct exec_core *exec, struct xocl_cmd *xcmd);
+};
+
+static struct exec_ops ert_ops;
+static struct exec_ops penguin_ops;
+
+/**
+ * struct exec_core: Core data structure for command execution on a device
+ *
+ * @ctx_list: Context list populated with device context
+ * @exec_lock: Lock for synchronizing external access
+ * @poll_wait_queue: Wait queue for device polling
+ * @scheduler: Command queue scheduler
+ * @submitted_cmds: Tracking of command submitted for execution on this device
+ * @num_slots: Number of command queue slots
+ * @num_cus: Number of CUs in loaded program
+ * @num_cdma: Number of CDMAs in hardware
+ * @polling_mode: If set then poll for command completion
+ * @cq_interrupt: If set then trigger interrupt to MB on new commands
+ * @configured: Flag to indicate that the core data structure has been initialized
+ * @stopped: Flag to indicate that the core data structure cannot be used
+ * @flush: Flag to indicate that commands for this device should be flushed
+ * @cu_usage: Usage count since last reset
+ * @slot_status: Bitmap to track status (busy(1)/free(0)) slots in command queue
+ * @ctrl_busy: Flag to indicate that slot 0 (ctrl commands) is busy
+ * @cu_status: Bitmap to track status (busy(1)/free(0)) of CUs. Unused in ERT mode.
+ * @sr0: If set, then status register [0..31] is pending with completed commands (ERT only).
+ * @sr1: If set, then status register [32..63] is pending with completed commands (ERT only).
+ * @sr2: If set, then status register [64..95] is pending with completed commands (ERT only).
+ * @sr3: If set, then status register [96..127] is pending with completed commands (ERT only).
+ * @ops: Scheduler operations vtable
+ */
+struct exec_core {
+	struct platform_device	   *pdev;
+
+	struct mutex		   exec_lock;
+
+	void __iomem		   *base;
+	u32			   intr_base;
+	u32			   intr_num;
+
+	wait_queue_head_t	   poll_wait_queue;
+
+	struct xocl_scheduler	   *scheduler;
+
+	uuid_t			   xclbin_id;
+
+	unsigned int		   num_slots;
+	unsigned int		   num_cus;
+	unsigned int		   num_cdma;
+	unsigned int		   polling_mode;
+	unsigned int		   cq_interrupt;
+	unsigned int		   configured;
+	unsigned int		   stopped;
+	unsigned int		   flush;
+
+	struct xocl_cu		   *cus[MAX_CUS];
+	struct xocl_ert		   *ert;
+
+	u32			   cu_usage[MAX_CUS];
+
+	// Bitmap tracks busy(1)/free(0) slots in cmd_slots
+	struct xocl_cmd		   *submitted_cmds[MAX_SLOTS];
+	DECLARE_BITMAP(slot_status, MAX_SLOTS);
+	unsigned int		   ctrl_busy;
+
+	// Status register pending complete.  Written by ISR,
+	// cleared by scheduler
+	atomic_t		   sr0;
+	atomic_t		   sr1;
+	atomic_t		   sr2;
+	atomic_t		   sr3;
+
+	// Operations for dynamic indirection dependt on MB
+	// or kernel scheduler
+	struct exec_ops		   *ops;
+
+	unsigned int		   uid;
+	unsigned int		   ip_reference[MAX_CUS];
+};
+
+/**
+ * exec_get_pdev() -
+ */
+static inline struct platform_device *
+exec_get_pdev(struct exec_core *exec)
+{
+	return exec->pdev;
+}
+
+/**
+ * exec_get_xdev() -
+ */
+static inline struct xocl_dev *
+exec_get_xdev(struct exec_core *exec)
+{
+	return xocl_get_xdev(exec->pdev);
+}
+
+/*
+ */
+static inline bool
+exec_is_ert(struct exec_core *exec)
+{
+	return exec->ops == &ert_ops;
+}
+
+/*
+ */
+static inline bool
+exec_is_polling(struct exec_core *exec)
+{
+	return exec->polling_mode;
+}
+
+/*
+ */
+static inline bool
+exec_is_flush(struct exec_core *exec)
+{
+	return exec->flush;
+}
+
+/*
+ */
+static inline u32
+exec_cu_base_addr(struct exec_core *exec, unsigned int cuidx)
+{
+	return cu_base_addr(exec->cus[cuidx]);
+}
+
+/*
+ */
+static inline u32
+exec_cu_usage(struct exec_core *exec, unsigned int cuidx)
+{
+	return exec->cu_usage[cuidx];
+}
+
+/*
+ */
+static void
+exec_cfg(struct exec_core *exec)
+{
+}
+
+/*
+ * to be automated
+ */
+static int
+exec_cfg_cmd(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	struct xocl_dev *xdev = exec_get_xdev(exec);
+	struct client_ctx *client = xcmd->client;
+	bool ert = xocl_mb_sched_on(xdev);
+	uint32_t *cdma = xocl_cdma_addr(xdev);
+	unsigned int dsa = xocl_dsa_version(xdev);
+	struct ert_configure_cmd *cfg;
+	int cuidx = 0;
+
+	/* Only allow configuration with one live ctx */
+	if (exec->configured) {
+		DRM_INFO("command scheduler is already configured for this device\n");
+		return 1;
+	}
+
+	DRM_INFO("ert per feature rom = %d\n", ert);
+	DRM_INFO("dsa per feature rom = %d\n", dsa);
+
+	cfg = (struct ert_configure_cmd *)(xcmd->ecmd);
+
+	/* Mark command as control command to force slot 0 execution */
+	cfg->type = ERT_CTRL;
+
+	if (cfg->count != 5 + cfg->num_cus) {
+		DRM_INFO("invalid configure command, count=%d expected 5+num_cus(%d)\n", cfg->count, cfg->num_cus);
+		return 1;
+	}
+
+	SCHED_DEBUG("configuring scheduler\n");
+	exec->num_slots = ERT_CQ_SIZE / cfg->slot_size;
+	exec->num_cus = cfg->num_cus;
+	exec->num_cdma = 0;
+
+	// skip this in polling mode
+	for (cuidx = 0; cuidx < exec->num_cus; ++cuidx) {
+		struct xocl_cu *xcu = exec->cus[cuidx];
+
+		if (!xcu)
+			xcu = exec->cus[cuidx] = cu_create();
+		cu_reset(xcu, cuidx, exec->base, cfg->data[cuidx]);
+		userpf_info(xdev, "%s cu(%d) at 0x%x\n", __func__, xcu->idx, xcu->addr);
+	}
+
+	if (cdma) {
+		uint32_t *addr = 0;
+
+		mutex_lock(&client->lock); /* for modification to client cu_bitmap */
+		for (addr = cdma; addr < cdma+4; ++addr) { /* 4 is from xclfeatures.h */
+			if (*addr) {
+				struct xocl_cu *xcu = exec->cus[cuidx];
+
+				if (!xcu)
+					xcu = exec->cus[cuidx] = cu_create();
+				cu_reset(xcu, cuidx, exec->base, *addr);
+				++exec->num_cus;
+				++exec->num_cdma;
+				++cfg->num_cus;
+				++cfg->count;
+				cfg->data[cuidx] = *addr;
+				set_bit(cuidx, client->cu_bitmap); /* cdma is shared */
+				userpf_info(xdev, "configure cdma as cu(%d) at 0x%x\n", cuidx, *addr);
+				++cuidx;
+			}
+		}
+		mutex_unlock(&client->lock);
+	}
+
+	if (ert && cfg->ert) {
+		SCHED_DEBUG("++ configuring embedded scheduler mode\n");
+		if (!exec->ert)
+			exec->ert = ert_create(exec->base, ERT_CQ_BASE_ADDR);
+		ert_cfg(exec->ert, cfg->slot_size, cfg->cq_int);
+		exec->ops = &ert_ops;
+		exec->polling_mode = cfg->polling;
+		exec->cq_interrupt = cfg->cq_int;
+		cfg->dsa52 = (dsa >= 52) ? 1 : 0;
+		cfg->cdma = cdma ? 1 : 0;
+	} else {
+		SCHED_DEBUG("++ configuring penguin scheduler mode\n");
+		exec->ops = &penguin_ops;
+		exec->polling_mode = 1;
+	}
+
+	// reserve slot 0 for control commands
+	set_bit(0, exec->slot_status);
+
+	DRM_INFO("scheduler config ert(%d) slots(%d), cudma(%d), cuisr(%d), cdma(%d), cus(%d)\n"
+		 , exec_is_ert(exec)
+		 , exec->num_slots
+		 , cfg->cu_dma ? 1 : 0
+		 , cfg->cu_isr ? 1 : 0
+		 , exec->num_cdma
+		 , exec->num_cus);
+
+	exec->configured = true;
+	return 0;
+}
+
+/**
+ * exec_reset() - Reset the scheduler
+ *
+ * @exec: Execution core (device) to reset
+ *
+ * TODO: Perform scheduler configuration based on current xclbin
+ *	 rather than relying of cfg command
+ */
+static void
+exec_reset(struct exec_core *exec)
+{
+	struct xocl_dev *xdev = exec_get_xdev(exec);
+	uuid_t *xclbin_id;
+
+	mutex_lock(&exec->exec_lock);
+
+	xclbin_id = (uuid_t *)xocl_icap_get_data(xdev, XCLBIN_UUID);
+
+	userpf_info(xdev, "%s(%d) cfg(%d)\n", __func__, exec->uid, exec->configured);
+
+	// only reconfigure the scheduler on new xclbin
+	if (!xclbin_id || (uuid_equal(&exec->xclbin_id, xclbin_id) && exec->configured)) {
+		exec->stopped = false;
+		exec->configured = false;  // TODO: remove, but hangs ERT because of in between AXI resets
+		goto out;
+	}
+
+	userpf_info(xdev, "exec->xclbin(%pUb),xclbin(%pUb)\n", &exec->xclbin_id, xclbin_id);
+	userpf_info(xdev, "%s resets for new xclbin", __func__);
+	memset(exec->cu_usage, 0, MAX_CUS * sizeof(u32));
+	uuid_copy(&exec->xclbin_id, xclbin_id);
+	exec->num_cus = 0;
+	exec->num_cdma = 0;
+
+	exec->num_slots = 16;
+	exec->polling_mode = 1;
+	exec->cq_interrupt = 0;
+	exec->configured = false;
+	exec->stopped = false;
+	exec->flush = false;
+	exec->ops = &penguin_ops;
+
+	bitmap_zero(exec->slot_status, MAX_SLOTS);
+	set_bit(0, exec->slot_status); // reserve for control command
+	exec->ctrl_busy = false;
+
+	atomic_set(&exec->sr0, 0);
+	atomic_set(&exec->sr1, 0);
+	atomic_set(&exec->sr2, 0);
+	atomic_set(&exec->sr3, 0);
+
+	exec_cfg(exec);
+
+out:
+	mutex_unlock(&exec->exec_lock);
+}
+
+/**
+ * exec_stop() - Stop the scheduler from scheduling commands on this core
+ *
+ * @exec:  Execution core (device) to stop
+ *
+ * Block access to current exec_core (device).	This API must be called prior
+ * to performing an AXI reset and downloading of a new xclbin.	Calling this
+ * API flushes the commands running on current device and prevents new
+ * commands from being scheduled on the device.	 This effectively prevents any
+ * further commands from running on the device
+ */
+static void
+exec_stop(struct exec_core *exec)
+{
+	int idx;
+	struct xocl_dev *xdev = exec_get_xdev(exec);
+	unsigned int outstanding = 0;
+	unsigned int wait_ms = 100;
+	unsigned int retry = 20;  // 2 sec
+
+	mutex_lock(&exec->exec_lock);
+	userpf_info(xdev, "%s(%p)\n", __func__, exec);
+	exec->stopped = true;
+	mutex_unlock(&exec->exec_lock);
+
+	// Wait for commands to drain if any
+	outstanding = atomic_read(&xdev->outstanding_execs);
+	while (--retry && outstanding) {
+		userpf_info(xdev, "Waiting for %d outstanding commands to finish", outstanding);
+		msleep(wait_ms);
+		outstanding = atomic_read(&xdev->outstanding_execs);
+	}
+
+	// Last gasp, flush any remaining commands for this device exec core
+	// This is an abnormal case.  All exec clients have been destroyed
+	// prior to exec_stop being called (per contract), this implies that
+	// all regular client commands have been flushed.
+	if (outstanding) {
+		// Wake up the scheduler to force one iteration flushing stale
+		// commands for this device
+		exec->flush = 1;
+		scheduler_intr(exec->scheduler);
+
+		// Wait a second
+		msleep(1000);
+	}
+
+	outstanding = atomic_read(&xdev->outstanding_execs);
+	if (outstanding)
+		userpf_err(xdev, "unexpected outstanding commands %d after flush", outstanding);
+
+	// Stale commands were flushed, reset submitted command state
+	for (idx = 0; idx < MAX_SLOTS; ++idx)
+		exec->submitted_cmds[idx] = NULL;
+
+	bitmap_zero(exec->slot_status, MAX_SLOTS);
+	set_bit(0, exec->slot_status); // reserve for control command
+	exec->ctrl_busy = false;
+}
+
+/*
+ */
+static irqreturn_t
+exec_isr(int irq, void *arg)
+{
+	struct exec_core *exec = (struct exec_core *)arg;
+
+	SCHED_DEBUGF("-> xocl_user_event %d\n", irq);
+	if (exec_is_ert(exec) && !exec->polling_mode) {
+
+		if (irq == 0)
+			atomic_set(&exec->sr0, 1);
+		else if (irq == 1)
+			atomic_set(&exec->sr1, 1);
+		else if (irq == 2)
+			atomic_set(&exec->sr2, 1);
+		else if (irq == 3)
+			atomic_set(&exec->sr3, 1);
+
+		/* wake up all scheduler ... currently one only */
+		scheduler_intr(exec->scheduler);
+	} else {
+		userpf_err(exec_get_xdev(exec), "Unhandled isr irq %d, is_ert %d, polling %d",
+			   irq, exec_is_ert(exec), exec->polling_mode);
+	}
+	SCHED_DEBUGF("<- xocl_user_event\n");
+	return IRQ_HANDLED;
+}
+
+/*
+ */
+struct exec_core *
+exec_create(struct platform_device *pdev, struct xocl_scheduler *xs)
+{
+	struct exec_core *exec = devm_kzalloc(&pdev->dev, sizeof(struct exec_core), GFP_KERNEL);
+	struct xocl_dev *xdev = xocl_get_xdev(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	static unsigned int count;
+	unsigned int i;
+
+	if (!exec)
+		return NULL;
+
+	mutex_init(&exec->exec_lock);
+	exec->base = xdev->core.bar_addr;
+
+	exec->intr_base = res->start;
+	exec->intr_num = res->end - res->start + 1;
+	exec->pdev = pdev;
+
+	init_waitqueue_head(&exec->poll_wait_queue);
+	exec->scheduler = xs;
+	exec->uid = count++;
+
+	for (i = 0; i < exec->intr_num; i++) {
+		xocl_user_interrupt_reg(xdev, i+exec->intr_base, exec_isr, exec);
+		xocl_user_interrupt_config(xdev, i + exec->intr_base, true);
+	}
+
+	exec_reset(exec);
+	platform_set_drvdata(pdev, exec);
+
+	SCHED_DEBUGF("%s(%d)\n", __func__, exec->uid);
+
+	return exec;
+}
+
+/*
+ */
+static void
+exec_destroy(struct exec_core *exec)
+{
+	int idx;
+
+	SCHED_DEBUGF("%s(%d)\n", __func__, exec->uid);
+	for (idx = 0; idx < exec->num_cus; ++idx)
+		cu_destroy(exec->cus[idx]);
+	if (exec->ert)
+		ert_destroy(exec->ert);
+	devm_kfree(&exec->pdev->dev, exec);
+}
+
+/*
+ */
+static inline struct xocl_scheduler *
+exec_scheduler(struct exec_core *exec)
+{
+	return exec->scheduler;
+}
+
+/*
+ * acquire_slot_idx() - First available slot index
+ */
+static unsigned int
+exec_acquire_slot_idx(struct exec_core *exec)
+{
+	unsigned int idx = find_first_zero_bit(exec->slot_status, MAX_SLOTS);
+
+	SCHED_DEBUGF("%s(%d) returns %d\n", __func__, exec->uid, idx < exec->num_slots ? idx : no_index);
+	if (idx < exec->num_slots) {
+		set_bit(idx, exec->slot_status);
+		return idx;
+	}
+	return no_index;
+}
+
+
+/**
+ * acquire_slot() - Acquire a slot index for a command
+ *
+ * This function makes a special case for control commands which
+ * must always dispatch to slot 0, otherwise normal acquisition
+ */
+static int
+exec_acquire_slot(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	// slot 0 is reserved for ctrl commands
+	if (cmd_type(xcmd) == ERT_CTRL) {
+		SCHED_DEBUGF("%s(%d,%lu) ctrl cmd\n", __func__, exec->uid, xcmd->uid);
+		if (exec->ctrl_busy)
+			return -1;
+		exec->ctrl_busy = true;
+		return (xcmd->slot_idx = 0);
+	}
+
+	return (xcmd->slot_idx = exec_acquire_slot_idx(exec));
+}
+
+/*
+ * release_slot_idx() - Release specified slot idx
+ */
+static void
+exec_release_slot_idx(struct exec_core *exec, unsigned int slot_idx)
+{
+	clear_bit(slot_idx, exec->slot_status);
+}
+
+/**
+ * release_slot() - Release a slot index for a command
+ *
+ * Special case for control commands that execute in slot 0.  This
+ * slot cannot be marked free ever.
+ */
+static void
+exec_release_slot(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	if (xcmd->slot_idx == no_index)
+		return; // already released
+
+	SCHED_DEBUGF("%s(%d) xcmd(%lu) slotidx(%d)\n",
+		     __func__, exec->uid, xcmd->uid, xcmd->slot_idx);
+	if (cmd_type(xcmd) == ERT_CTRL) {
+		SCHED_DEBUG("+ ctrl cmd\n");
+		exec->ctrl_busy = false;
+	} else {
+		exec_release_slot_idx(exec, xcmd->slot_idx);
+	}
+	xcmd->slot_idx = no_index;
+}
+
+/*
+ * submit_cmd() - Submit command for execution on this core
+ *
+ * Return: true on success, false if command could not be submitted
+ */
+static bool
+exec_submit_cmd(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	unsigned int slotidx = exec_acquire_slot(exec, xcmd);
+
+	if (slotidx == no_index)
+		return false;
+	SCHED_DEBUGF("%s(%d,%lu) slotidx(%d)\n", __func__, exec->uid, xcmd->uid, slotidx);
+	exec->submitted_cmds[slotidx] = xcmd;
+	cmd_set_int_state(xcmd, ERT_CMD_STATE_SUBMITTED);
+	return true;
+}
+
+/*
+ * finish_cmd() - Special post processing of commands after execution
+ */
+static int
+exec_finish_cmd(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	if (cmd_opcode(xcmd) == ERT_CU_STAT && exec_is_ert(exec))
+		ert_read_custat(exec->ert, exec->num_cus, exec->cu_usage, xcmd);
+	return 0;
+}
+
+/*
+ * execute_write_cmd() - Execute ERT_WRITE commands
+ */
+static int
+exec_execute_write_cmd(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	struct ert_packet *ecmd = xcmd->ecmd;
+	unsigned int idx = 0;
+
+	SCHED_DEBUGF("-> %s(%d,%lu)\n", __func__, exec->uid, xcmd->uid);
+	for (idx = 0; idx < ecmd->count - 1; idx += 2) {
+		u32 addr = ecmd->data[idx];
+		u32 val = ecmd->data[idx+1];
+
+		SCHED_DEBUGF("+ exec_write_cmd base[0x%x] = 0x%x\n", addr, val);
+		iowrite32(val, exec->base + addr);
+	}
+	SCHED_DEBUG("<- exec_write\n");
+	return 0;
+}
+
+/*
+ * notify_host() - Notify user space that a command is complete.
+ */
+static void
+exec_notify_host(struct exec_core *exec)
+{
+	struct list_head *ptr;
+	struct client_ctx *entry;
+	struct xocl_dev *xdev = exec_get_xdev(exec);
+
+	SCHED_DEBUGF("-> %s(%d)\n", __func__, exec->uid);
+
+	/* now for each client update the trigger counter in the context */
+	mutex_lock(&xdev->ctx_list_lock);
+	list_for_each(ptr, &xdev->ctx_list) {
+		entry = list_entry(ptr, struct client_ctx, link);
+		atomic_inc(&entry->trigger);
+	}
+	mutex_unlock(&xdev->ctx_list_lock);
+	/* wake up all the clients */
+	wake_up_interruptible(&exec->poll_wait_queue);
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
+
+/*
+ * exec_cmd_mark_complete() - Move a command to complete state
+ *
+ * Commands are marked complete in two ways
+ *  1. Through polling of CUs or polling of MB status register
+ *  2. Through interrupts from MB
+ *
+ * @xcmd: Command to mark complete
+ *
+ * The external command state is changed to complete and the host
+ * is notified that some command has completed.
+ */
+static void
+exec_mark_cmd_complete(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	SCHED_DEBUGF("-> %s(%d,%lu)\n", __func__, exec->uid, xcmd->uid);
+	if (cmd_type(xcmd) == ERT_CTRL)
+		exec_finish_cmd(exec, xcmd);
+
+	cmd_set_state(xcmd, ERT_CMD_STATE_COMPLETED);
+
+	if (exec->polling_mode)
+		scheduler_decr_poll(exec->scheduler);
+
+	exec_release_slot(exec, xcmd);
+	exec_notify_host(exec);
+
+	// Deactivate command and trigger chain of waiting commands
+	cmd_mark_deactive(xcmd);
+	cmd_trigger_chain(xcmd);
+
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
+
+/**
+ * mark_mask_complete() - Move all commands in mask to complete state
+ *
+ * @mask: Bitmask with queried statuses of commands
+ * @mask_idx: Index of the command mask. Used to offset the actual cmd slot index
+ *
+ * Used in ERT mode only.  Currently ERT submitted commands remain in exec
+ * submitted queue as ERT doesn't support data flow
+ */
+static void
+exec_mark_mask_complete(struct exec_core *exec, u32 mask, unsigned int mask_idx)
+{
+	int bit_idx = 0, cmd_idx = 0;
+
+	SCHED_DEBUGF("-> %s(0x%x,%d)\n", __func__, mask, mask_idx);
+	if (!mask)
+		return;
+
+	for (bit_idx = 0, cmd_idx = mask_idx<<5; bit_idx < 32; mask >>= 1, ++bit_idx, ++cmd_idx) {
+		// mask could be -1 when firewall trips, double check
+		// exec->submitted_cmds[cmd_idx] to make sure it's not NULL
+		if ((mask & 0x1) && exec->submitted_cmds[cmd_idx])
+			exec_mark_cmd_complete(exec, exec->submitted_cmds[cmd_idx]);
+	}
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
+
+/*
+ * penguin_start_cmd() - Start a command in penguin mode
+ */
+static bool
+exec_penguin_start_cmd(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	unsigned int cuidx;
+	u32 opcode = cmd_opcode(xcmd);
+
+	SCHED_DEBUGF("-> %s (%d,%lu) opcode(%d)\n", __func__, exec->uid, xcmd->uid, opcode);
+
+	if (opcode == ERT_WRITE && exec_execute_write_cmd(exec, xcmd)) {
+		cmd_set_state(xcmd, ERT_CMD_STATE_ERROR);
+		return false;
+	}
+
+	if (opcode != ERT_START_CU) {
+		SCHED_DEBUGF("<- %s -> true\n", __func__);
+		return true;
+	}
+
+	// Find a ready CU
+	for (cuidx = 0; cuidx < exec->num_cus; ++cuidx) {
+		struct xocl_cu *xcu = exec->cus[cuidx];
+
+		if (cmd_has_cu(xcmd, cuidx) && cu_ready(xcu) && cu_start(xcu, xcmd)) {
+			exec->submitted_cmds[xcmd->slot_idx] = NULL;
+			++exec->cu_usage[cuidx];
+			exec_release_slot(exec, xcmd);
+			xcmd->cu_idx = cuidx;
+			SCHED_DEBUGF("<- %s -> true\n", __func__);
+			return true;
+		}
+	}
+	SCHED_DEBUGF("<- %s -> false\n", __func__);
+	return false;
+}
+
+/**
+ * penguin_query() - Check command status of argument command
+ *
+ * @xcmd: Command to check
+ *
+ * Function is called in penguin mode (no embedded scheduler).
+ */
+static void
+exec_penguin_query_cmd(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	u32 cmdopcode = cmd_opcode(xcmd);
+	u32 cmdtype = cmd_type(xcmd);
+
+	SCHED_DEBUGF("-> %s(%lu) opcode(%d) type(%d) slot_idx=%d\n",
+		     __func__, xcmd->uid, cmdopcode, cmdtype, xcmd->slot_idx);
+
+	if (cmdtype == ERT_KDS_LOCAL || cmdtype == ERT_CTRL)
+		exec_mark_cmd_complete(exec, xcmd);
+	else if (cmdopcode == ERT_START_CU) {
+		struct xocl_cu *xcu = exec->cus[xcmd->cu_idx];
+
+		if (cu_first_done(xcu) == xcmd) {
+			cu_pop_done(xcu);
+			exec_mark_cmd_complete(exec, xcmd);
+		}
+	}
+
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
+
+
+/*
+ * ert_start_cmd() - Start a command on ERT
+ */
+static bool
+exec_ert_start_cmd(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	// if (cmd_type(xcmd) == ERT_DATAFLOW)
+	//   exec_penguin_start_cmd(exec,xcmd);
+	return ert_start_cmd(exec->ert, xcmd);
+}
+
+/*
+ * ert_query_cmd() - Check command completion in ERT
+ *
+ * @xcmd: Command to check
+ *
+ * This function is for ERT mode.  In polling mode, check the command status
+ * register containing the slot assigned to the command.  In interrupt mode
+ * check the interrupting status register.  The function checks all commands
+ * in the same command status register as argument command so more than one
+ * command may be marked complete by this function.
+ */
+static void
+exec_ert_query_cmd(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	unsigned int cmd_mask_idx = slot_mask_idx(xcmd->slot_idx);
+
+	SCHED_DEBUGF("-> %s(%lu) slot_idx(%d), cmd_mask_idx(%d)\n", __func__, xcmd->uid, xcmd->slot_idx, cmd_mask_idx);
+
+	if (cmd_type(xcmd) == ERT_KDS_LOCAL) {
+		exec_mark_cmd_complete(exec, xcmd);
+		SCHED_DEBUGF("<- %s local command\n", __func__);
+		return;
+	}
+
+	if (exec->polling_mode
+	    || (cmd_mask_idx == 0 && atomic_xchg(&exec->sr0, 0))
+	    || (cmd_mask_idx == 1 && atomic_xchg(&exec->sr1, 0))
+	    || (cmd_mask_idx == 2 && atomic_xchg(&exec->sr2, 0))
+	    || (cmd_mask_idx == 3 && atomic_xchg(&exec->sr3, 0))) {
+		u32 csr_addr = ERT_STATUS_REGISTER_ADDR + (cmd_mask_idx<<2);
+		u32 mask = ioread32(xcmd->exec->base + csr_addr);
+
+		SCHED_DEBUGF("++ %s csr_addr=0x%x mask=0x%x\n", __func__, csr_addr, mask);
+		if (mask)
+			exec_mark_mask_complete(xcmd->exec, mask, cmd_mask_idx);
+	}
+
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
+
+/*
+ * start_cmd() - Start execution of a command
+ *
+ * Return: true if successfully started, false otherwise
+ *
+ * Function dispatches based on penguin vs ert mode
+ */
+static bool
+exec_start_cmd(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	// assert cmd had been submitted
+	SCHED_DEBUGF("%s(%d,%lu) opcode(%d)\n", __func__, exec->uid, xcmd->uid, cmd_opcode(xcmd));
+
+	if (exec->ops->start(exec, xcmd)) {
+		cmd_set_int_state(xcmd, ERT_CMD_STATE_RUNNING);
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * query_cmd() - Check status of command
+ *
+ * Function dispatches based on penguin vs ert mode.  In ERT mode
+ * multiple commands can be marked complete by this function.
+ */
+static void
+exec_query_cmd(struct exec_core *exec, struct xocl_cmd *xcmd)
+{
+	SCHED_DEBUGF("%s(%d,%lu)\n", __func__, exec->uid, xcmd->uid);
+	exec->ops->query(exec, xcmd);
+}
+
+
+
+/**
+ * ert_ops: operations for ERT scheduling
+ */
+static struct exec_ops ert_ops = {
+	.start = exec_ert_start_cmd,
+	.query = exec_ert_query_cmd,
+};
+
+/**
+ * penguin_ops: operations for kernel mode scheduling
+ */
+static struct exec_ops penguin_ops = {
+	.start = exec_penguin_start_cmd,
+	.query = exec_penguin_query_cmd,
+};
+
+/*
+ */
+static inline struct exec_core *
+pdev_get_exec(struct platform_device *pdev)
+{
+	return platform_get_drvdata(pdev);
+}
+
+/*
+ */
+static inline struct exec_core *
+dev_get_exec(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+
+	return pdev ? pdev_get_exec(pdev) : NULL;
+}
+
+/*
+ */
+static inline struct xocl_dev *
+dev_get_xdev(struct device *dev)
+{
+	struct exec_core *exec = dev_get_exec(dev);
+
+	return exec ? exec_get_xdev(exec) : NULL;
+}
+
+/**
+ * List of new pending xocl_cmd objects
+ *
+ * @pending_cmds: populated from user space with new commands for buffer objects
+ * @num_pending: number of pending commands
+ *
+ * Scheduler copies pending commands to its private queue when necessary
+ */
+static LIST_HEAD(pending_cmds);
+static DEFINE_MUTEX(pending_cmds_mutex);
+static atomic_t num_pending = ATOMIC_INIT(0);
+
+static void
+pending_cmds_reset(void)
+{
+	/* clear stale command objects if any */
+	while (!list_empty(&pending_cmds)) {
+		struct xocl_cmd *xcmd = list_first_entry(&pending_cmds, struct xocl_cmd, cq_list);
+
+		DRM_INFO("deleting stale pending cmd\n");
+		cmd_free(xcmd);
+	}
+	atomic_set(&num_pending, 0);
+}
+
+/**
+ * struct xocl_sched: scheduler for xocl_cmd objects
+ *
+ * @scheduler_thread: thread associated with this scheduler
+ * @use_count: use count for this scheduler
+ * @wait_queue: conditional wait queue for scheduler thread
+ * @error: set to 1 to indicate scheduler error
+ * @stop: set to 1 to indicate scheduler should stop
+ * @reset: set to 1 to reset the scheduler
+ * @command_queue: list of command objects managed by scheduler
+ * @intc: boolean flag set when there is a pending interrupt for command completion
+ * @poll: number of running commands in polling mode
+ */
+struct xocl_scheduler {
+	struct task_struct	  *scheduler_thread;
+	unsigned int		   use_count;
+
+	wait_queue_head_t	   wait_queue;
+	unsigned int		   error;
+	unsigned int		   stop;
+	unsigned int		   reset;
+
+	struct list_head	   command_queue;
+
+	unsigned int		   intc; /* pending intr shared with isr, word aligned atomic */
+	unsigned int		   poll; /* number of cmds to poll */
+};
+
+static struct xocl_scheduler scheduler0;
+
+static void
+scheduler_reset(struct xocl_scheduler *xs)
+{
+	xs->error = 0;
+	xs->stop = 0;
+	xs->poll = 0;
+	xs->reset = false;
+	xs->intc = 0;
+}
+
+static void
+scheduler_cq_reset(struct xocl_scheduler *xs)
+{
+	while (!list_empty(&xs->command_queue)) {
+		struct xocl_cmd *xcmd = list_first_entry(&xs->command_queue, struct xocl_cmd, cq_list);
+
+		DRM_INFO("deleting stale scheduler cmd\n");
+		cmd_free(xcmd);
+	}
+}
+
+static void
+scheduler_wake_up(struct xocl_scheduler *xs)
+{
+	wake_up_interruptible(&xs->wait_queue);
+}
+
+static void
+scheduler_intr(struct xocl_scheduler *xs)
+{
+	xs->intc = 1;
+	scheduler_wake_up(xs);
+}
+
+static inline void
+scheduler_decr_poll(struct xocl_scheduler *xs)
+{
+	--xs->poll;
+}
+
+
+/**
+ * scheduler_queue_cmds() - Queue any pending commands
+ *
+ * The scheduler copies pending commands to its internal command queue where
+ * is is now in queued state.
+ */
+static void
+scheduler_queue_cmds(struct xocl_scheduler *xs)
+{
+	struct xocl_cmd *xcmd;
+	struct list_head *pos, *next;
+
+	SCHED_DEBUGF("-> %s\n", __func__);
+	mutex_lock(&pending_cmds_mutex);
+	list_for_each_safe(pos, next, &pending_cmds) {
+		xcmd = list_entry(pos, struct xocl_cmd, cq_list);
+		if (xcmd->xs != xs)
+			continue;
+		SCHED_DEBUGF("+ queueing cmd(%lu)\n", xcmd->uid);
+		list_del(&xcmd->cq_list);
+		list_add_tail(&xcmd->cq_list, &xs->command_queue);
+
+		/* chain active dependencies if any to this command object */
+		if (cmd_wait_count(xcmd) && cmd_chain_dependencies(xcmd))
+			cmd_set_state(xcmd, ERT_CMD_STATE_ERROR);
+		else
+			cmd_set_int_state(xcmd, ERT_CMD_STATE_QUEUED);
+
+		/* this command is now active and can chain other commands */
+		cmd_mark_active(xcmd);
+		atomic_dec(&num_pending);
+	}
+	mutex_unlock(&pending_cmds_mutex);
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
+
+/**
+ * queued_to_running() - Move a command from queued to running state if possible
+ *
+ * @xcmd: Command to start
+ *
+ * Upon success, the command is not necessarily running. In ert mode the
+ * command will have been submitted to the embedded scheduler, whereas in
+ * penguin mode the command has been started on a CU.
+ *
+ * Return: %true if command was submitted to device, %false otherwise
+ */
+static bool
+scheduler_queued_to_submitted(struct xocl_scheduler *xs, struct xocl_cmd *xcmd)
+{
+	struct exec_core *exec = cmd_exec(xcmd);
+	bool retval = false;
+
+	if (cmd_wait_count(xcmd))
+		return false;
+
+	SCHED_DEBUGF("-> %s(%lu) opcode(%d)\n", __func__, xcmd->uid, cmd_opcode(xcmd));
+
+	// configure prior to using the core
+	if (cmd_opcode(xcmd) == ERT_CONFIGURE && exec_cfg_cmd(exec, xcmd)) {
+		cmd_set_state(xcmd, ERT_CMD_STATE_ERROR);
+		return false;
+	}
+
+	// submit the command
+	if (exec_submit_cmd(exec, xcmd)) {
+		if (exec->polling_mode)
+			++xs->poll;
+		retval = true;
+	}
+
+	SCHED_DEBUGF("<- queued_to_submitted returns %d\n", retval);
+
+	return retval;
+}
+
+static bool
+scheduler_submitted_to_running(struct xocl_scheduler *xs, struct xocl_cmd *xcmd)
+{
+	return exec_start_cmd(cmd_exec(xcmd), xcmd);
+}
+
+/**
+ * running_to_complete() - Check status of running commands
+ *
+ * @xcmd: Command is in running state
+ *
+ * When ERT is enabled this function may mark more than just argument
+ * command as complete based on content of command completion register.
+ * Without ERT, only argument command is checked for completion.
+ */
+static void
+scheduler_running_to_complete(struct xocl_scheduler *xs, struct xocl_cmd *xcmd)
+{
+	exec_query_cmd(cmd_exec(xcmd), xcmd);
+}
+
+/**
+ * complete_to_free() - Recycle a complete command objects
+ *
+ * @xcmd: Command is in complete state
+ */
+static void
+scheduler_complete_to_free(struct xocl_scheduler *xs, struct xocl_cmd *xcmd)
+{
+	SCHED_DEBUGF("-> %s(%lu)\n", __func__, xcmd->uid);
+	cmd_free(xcmd);
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
+
+static void
+scheduler_error_to_free(struct xocl_scheduler *xs, struct xocl_cmd *xcmd)
+{
+	SCHED_DEBUGF("-> %s(%lu)\n", __func__, xcmd->uid);
+	exec_notify_host(cmd_exec(xcmd));
+	scheduler_complete_to_free(xs, xcmd);
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
+
+static void
+scheduler_abort_to_free(struct xocl_scheduler *xs, struct xocl_cmd *xcmd)
+{
+	SCHED_DEBUGF("-> %s(%lu)\n", __func__, xcmd->uid);
+	scheduler_error_to_free(xs, xcmd);
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
+
+/**
+ * scheduler_iterator_cmds() - Iterate all commands in scheduler command queue
+ */
+static void
+scheduler_iterate_cmds(struct xocl_scheduler *xs)
+{
+	struct list_head *pos, *next;
+
+	SCHED_DEBUGF("-> %s\n", __func__);
+	list_for_each_safe(pos, next, &xs->command_queue) {
+		struct xocl_cmd *xcmd = list_entry(pos, struct xocl_cmd, cq_list);
+
+		cmd_update_state(xcmd);
+		SCHED_DEBUGF("+ processing cmd(%lu)\n", xcmd->uid);
+
+		/* check running first since queued maybe we waiting for cmd slot */
+		if (xcmd->state == ERT_CMD_STATE_QUEUED)
+			scheduler_queued_to_submitted(xs, xcmd);
+		if (xcmd->state == ERT_CMD_STATE_SUBMITTED)
+			scheduler_submitted_to_running(xs, xcmd);
+		if (xcmd->state == ERT_CMD_STATE_RUNNING)
+			scheduler_running_to_complete(xs, xcmd);
+		if (xcmd->state == ERT_CMD_STATE_COMPLETED)
+			scheduler_complete_to_free(xs, xcmd);
+		if (xcmd->state == ERT_CMD_STATE_ERROR)
+			scheduler_error_to_free(xs, xcmd);
+		if (xcmd->state == ERT_CMD_STATE_ABORT)
+			scheduler_abort_to_free(xs, xcmd);
+	}
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
+
+/**
+ * scheduler_wait_condition() - Check status of scheduler wait condition
+ *
+ * Scheduler must wait (sleep) if
+ *   1. there are no pending commands
+ *   2. no pending interrupt from embedded scheduler
+ *   3. no pending complete commands in polling mode
+ *
+ * Return: 1 if scheduler must wait, 0 othewise
+ */
+static int
+scheduler_wait_condition(struct xocl_scheduler *xs)
+{
+	if (kthread_should_stop()) {
+		xs->stop = 1;
+		SCHED_DEBUG("scheduler wakes kthread_should_stop\n");
+		return 0;
+	}
+
+	if (atomic_read(&num_pending)) {
+		SCHED_DEBUG("scheduler wakes to copy new pending commands\n");
+		return 0;
+	}
+
+	if (xs->intc) {
+		SCHED_DEBUG("scheduler wakes on interrupt\n");
+		xs->intc = 0;
+		return 0;
+	}
+
+	if (xs->poll) {
+		SCHED_DEBUG("scheduler wakes to poll\n");
+		return 0;
+	}
+
+	SCHED_DEBUG("scheduler waits ...\n");
+	return 1;
+}
+
+/**
+ * scheduler_wait() - check if scheduler should wait
+ *
+ * See scheduler_wait_condition().
+ */
+static void
+scheduler_wait(struct xocl_scheduler *xs)
+{
+	wait_event_interruptible(xs->wait_queue, scheduler_wait_condition(xs) == 0);
+}
+
+/**
+ * scheduler_loop() - Run one loop of the scheduler
+ */
+static void
+scheduler_loop(struct xocl_scheduler *xs)
+{
+	static unsigned int loop_cnt;
+
+	SCHED_DEBUGF("%s\n", __func__);
+	scheduler_wait(xs);
+
+	if (xs->error)
+		DRM_INFO("scheduler encountered unexpected error\n");
+
+	if (xs->stop)
+		return;
+
+	if (xs->reset) {
+		SCHED_DEBUG("scheduler is resetting after timeout\n");
+		scheduler_reset(xs);
+	}
+
+	/* queue new pending commands */
+	scheduler_queue_cmds(xs);
+
+	/* iterate all commands */
+	scheduler_iterate_cmds(xs);
+
+	// loop 8 times before explicitly yielding
+	if (++loop_cnt == 8) {
+		loop_cnt = 0;
+		schedule();
+	}
+}
+
+/**
+ * scheduler() - Command scheduler thread routine
+ */
+static int
+scheduler(void *data)
+{
+	struct xocl_scheduler *xs = (struct xocl_scheduler *)data;
+
+	while (!xs->stop)
+		scheduler_loop(xs);
+	DRM_INFO("%s:%d %s thread exits with value %d\n", __FILE__, __LINE__, __func__, xs->error);
+	return xs->error;
+}
+
+
+
+/**
+ * add_xcmd() - Add initialized xcmd object to pending command list
+ *
+ * @xcmd: Command to add
+ *
+ * Scheduler copies pending commands to its internal command queue.
+ *
+ * Return: 0 on success
+ */
+static int
+add_xcmd(struct xocl_cmd *xcmd)
+{
+	struct exec_core *exec = xcmd->exec;
+	struct xocl_dev *xdev = xocl_get_xdev(exec->pdev);
+
+	// Prevent stop and reset
+	mutex_lock(&exec->exec_lock);
+
+	SCHED_DEBUGF("-> %s(%lu) pid(%d)\n", __func__, xcmd->uid, pid_nr(task_tgid(current)));
+	SCHED_DEBUGF("+ exec stopped(%d) configured(%d)\n", exec->stopped, exec->configured);
+
+	if (exec->stopped || (!exec->configured && cmd_opcode(xcmd) != ERT_CONFIGURE))
+		goto err;
+
+	cmd_set_state(xcmd, ERT_CMD_STATE_NEW);
+	mutex_lock(&pending_cmds_mutex);
+	list_add_tail(&xcmd->cq_list, &pending_cmds);
+	atomic_inc(&num_pending);
+	mutex_unlock(&pending_cmds_mutex);
+
+	/* wake scheduler */
+	atomic_inc(&xdev->outstanding_execs);
+	atomic64_inc(&xdev->total_execs);
+	scheduler_wake_up(xcmd->xs);
+
+	SCHED_DEBUGF("<- %s ret(0) opcode(%d) type(%d) num_pending(%d)\n",
+		     __func__, cmd_opcode(xcmd), cmd_type(xcmd), atomic_read(&num_pending));
+	mutex_unlock(&exec->exec_lock);
+	return 0;
+
+err:
+	SCHED_DEBUGF("<- %s ret(1) opcode(%d) type(%d) num_pending(%d)\n",
+		     __func__, cmd_opcode(xcmd), cmd_type(xcmd), atomic_read(&num_pending));
+	mutex_unlock(&exec->exec_lock);
+	return 1;
+}
+
+
+/**
+ * add_bo_cmd() - Add a new buffer object command to pending list
+ *
+ * @exec: Targeted device
+ * @client: Client context
+ * @bo: Buffer objects from user space from which new command is created
+ * @numdeps: Number of dependencies for this command
+ * @deps: List of @numdeps dependencies
+ *
+ * Scheduler copies pending commands to its internal command queue.
+ *
+ * Return: 0 on success, 1 on failure
+ */
+static int
+add_bo_cmd(struct exec_core *exec, struct client_ctx *client, struct drm_xocl_bo *bo,
+	   int numdeps, struct drm_xocl_bo **deps)
+{
+	struct xocl_cmd *xcmd = cmd_get(exec_scheduler(exec), exec, client);
+
+	if (!xcmd)
+		return 1;
+
+	SCHED_DEBUGF("-> %s(%lu)\n", __func__, xcmd->uid);
+
+	cmd_bo_init(xcmd, bo, numdeps, deps, !exec_is_ert(exec));
+
+	if (add_xcmd(xcmd))
+		goto err;
+
+	SCHED_DEBUGF("<- %s ret(0) opcode(%d) type(%d)\n", __func__, cmd_opcode(xcmd), cmd_type(xcmd));
+	return 0;
+err:
+	cmd_abort(xcmd);
+	SCHED_DEBUGF("<- %s ret(1) opcode(%d) type(%d)\n", __func__, cmd_opcode(xcmd), cmd_type(xcmd));
+	return 1;
+}
+
+static int
+add_ctrl_cmd(struct exec_core *exec, struct client_ctx *client, struct ert_packet *packet)
+{
+	struct xocl_cmd *xcmd = cmd_get(exec_scheduler(exec), exec, client);
+
+	if (!xcmd)
+		return 1;
+
+	SCHED_DEBUGF("-> %s(%lu)\n", __func__, xcmd->uid);
+
+	cmd_packet_init(xcmd, packet);
+
+	if (add_xcmd(xcmd))
+		goto err;
+
+	SCHED_DEBUGF("<- %s ret(0) opcode(%d) type(%d)\n", __func__, cmd_opcode(xcmd), cmd_type(xcmd));
+	return 0;
+err:
+	cmd_abort(xcmd);
+	SCHED_DEBUGF("<- %s ret(1) opcode(%d) type(%d)\n", __func__, cmd_opcode(xcmd), cmd_type(xcmd));
+	return 1;
+}
+
+
+/**
+ * init_scheduler_thread() - Initialize scheduler thread if necessary
+ *
+ * Return: 0 on success, -errno otherwise
+ */
+static int
+init_scheduler_thread(struct xocl_scheduler *xs)
+{
+	SCHED_DEBUGF("%s use_count=%d\n", __func__, xs->use_count);
+	if (xs->use_count++)
+		return 0;
+
+	init_waitqueue_head(&xs->wait_queue);
+	INIT_LIST_HEAD(&xs->command_queue);
+	scheduler_reset(xs);
+
+	xs->scheduler_thread = kthread_run(scheduler, (void *)xs, "xocl-scheduler-thread0");
+	if (IS_ERR(xs->scheduler_thread)) {
+		int ret = PTR_ERR(xs->scheduler_thread);
+
+		DRM_ERROR(__func__);
+		return ret;
+	}
+	return 0;
+}
+
+/**
+ * fini_scheduler_thread() - Finalize scheduler thread if unused
+ *
+ * Return: 0 on success, -errno otherwise
+ */
+static int
+fini_scheduler_thread(struct xocl_scheduler *xs)
+{
+	int retval = 0;
+
+	SCHED_DEBUGF("%s use_count=%d\n", __func__, xs->use_count);
+	if (--xs->use_count)
+		return 0;
+
+	retval = kthread_stop(xs->scheduler_thread);
+
+	/* clear stale command objects if any */
+	pending_cmds_reset();
+	scheduler_cq_reset(xs);
+
+	/* reclaim memory for allocate command objects */
+	cmd_list_delete();
+
+	return retval;
+}
+
+/**
+ * Entry point for exec buffer.
+ *
+ * Function adds exec buffer to the pending list of commands
+ */
+int
+add_exec_buffer(struct platform_device *pdev, struct client_ctx *client, void *buf,
+		int numdeps, struct drm_xocl_bo **deps)
+{
+	struct exec_core *exec = platform_get_drvdata(pdev);
+	// Add the command to pending list
+	return add_bo_cmd(exec, client, buf, numdeps, deps);
+}
+
+static int
+xocl_client_lock_bitstream_nolock(struct xocl_dev *xdev, struct client_ctx *client)
+{
+	int pid = pid_nr(task_tgid(current));
+	uuid_t *xclbin_id;
+
+	if (client->xclbin_locked)
+		return 0;
+
+	xclbin_id = (uuid_t *)xocl_icap_get_data(xdev, XCLBIN_UUID);
+	if (!xclbin_id || !uuid_equal(xclbin_id, &client->xclbin_id)) {
+		userpf_err(xdev,
+			   "device xclbin does not match context xclbin, cannot obtain lock for process %d",
+			   pid);
+		return 1;
+	}
+
+	if (xocl_icap_lock_bitstream(xdev, &client->xclbin_id, pid) < 0) {
+		userpf_err(xdev, "could not lock bitstream for process %d", pid);
+		return 1;
+	}
+
+	client->xclbin_locked = true;
+	userpf_info(xdev, "process %d successfully locked xcblin", pid);
+	return 0;
+}
+
+static int
+xocl_client_lock_bitstream(struct xocl_dev *xdev, struct client_ctx *client)
+{
+	int ret = 0;
+
+	mutex_lock(&client->lock);	   // protect current client
+	mutex_lock(&xdev->ctx_list_lock);  // protect xdev->xclbin_id
+	ret = xocl_client_lock_bitstream_nolock(xdev, client);
+	mutex_unlock(&xdev->ctx_list_lock);
+	mutex_unlock(&client->lock);
+	return ret;
+}
+
+
+static int
+create_client(struct platform_device *pdev, void **priv)
+{
+	struct client_ctx	*client;
+	struct xocl_dev		*xdev = xocl_get_xdev(pdev);
+	int			ret = 0;
+
+	client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
+	if (!client)
+		return -ENOMEM;
+
+	mutex_lock(&xdev->ctx_list_lock);
+
+	if (!xdev->offline) {
+		client->pid = task_tgid(current);
+		mutex_init(&client->lock);
+		client->xclbin_locked = false;
+		client->abort = false;
+		atomic_set(&client->trigger, 0);
+		atomic_set(&client->outstanding_execs, 0);
+		client->num_cus = 0;
+		client->xdev = xocl_get_xdev(pdev);
+		list_add_tail(&client->link, &xdev->ctx_list);
+		*priv =	 client;
+	} else {
+		/* Do not allow new client to come in while being offline. */
+		devm_kfree(&pdev->dev, client);
+		ret = -EBUSY;
+	}
+
+	mutex_unlock(&xdev->ctx_list_lock);
+
+	DRM_INFO("creating scheduler client for pid(%d), ret: %d\n",
+		 pid_nr(task_tgid(current)), ret);
+
+	return ret;
+}
+
+static void destroy_client(struct platform_device *pdev, void **priv)
+{
+	struct client_ctx *client = (struct client_ctx *)(*priv);
+	struct exec_core *exec = platform_get_drvdata(pdev);
+	struct xocl_scheduler *xs = exec_scheduler(exec);
+	struct xocl_dev	*xdev = xocl_get_xdev(pdev);
+	unsigned int	outstanding = atomic_read(&client->outstanding_execs);
+	unsigned int	timeout_loops = 20;
+	unsigned int	loops = 0;
+	int pid = pid_nr(task_tgid(current));
+	unsigned int bit;
+	struct ip_layout *layout = XOCL_IP_LAYOUT(xdev);
+
+	bit = layout
+	  ? find_first_bit(client->cu_bitmap, layout->m_count)
+	  : MAX_CUS;
+
+	/*
+	 * This happens when application exists without formally releasing the
+	 * contexts on CUs. Give up our contexts on CUs and our lock on xclbin.
+	 * Note, that implicit CUs (such as CDMA) do not add to ip_reference.
+	 */
+	while (layout && (bit < layout->m_count)) {
+		if (exec->ip_reference[bit]) {
+			userpf_info(xdev, "CTX reclaim (%pUb, %d, %u)",
+				&client->xclbin_id, pid, bit);
+			exec->ip_reference[bit]--;
+		}
+		bit = find_next_bit(client->cu_bitmap, layout->m_count, bit + 1);
+	}
+	bitmap_zero(client->cu_bitmap, MAX_CUS);
+
+	// force scheduler to abort execs for this client
+	client->abort = true;
+
+	// wait for outstanding execs to finish
+	while (outstanding) {
+		unsigned int new;
+
+		userpf_info(xdev, "waiting for %d outstanding execs to finish", outstanding);
+		msleep(500);
+		new = atomic_read(&client->outstanding_execs);
+		loops = (new == outstanding ? (loops + 1) : 0);
+		if (loops == timeout_loops) {
+			userpf_err(xdev,
+				   "Giving up with %d outstanding execs, please reset device with 'xbutil reset'\n",
+				   outstanding);
+			xdev->needs_reset = true;
+			// reset the scheduler loop
+			xs->reset = true;
+			break;
+		}
+		outstanding = new;
+	}
+
+	DRM_INFO("client exits pid(%d)\n", pid);
+
+	mutex_lock(&xdev->ctx_list_lock);
+	list_del(&client->link);
+	mutex_unlock(&xdev->ctx_list_lock);
+
+	if (client->xclbin_locked)
+		xocl_icap_unlock_bitstream(xdev, &client->xclbin_id, pid);
+	mutex_destroy(&client->lock);
+	devm_kfree(&pdev->dev, client);
+	*priv = NULL;
+}
+
+static uint poll_client(struct platform_device *pdev, struct file *filp,
+	poll_table *wait, void *priv)
+{
+	struct client_ctx	*client = (struct client_ctx *)priv;
+	struct exec_core	*exec;
+	int			counter;
+	uint			ret = 0;
+
+	exec = platform_get_drvdata(pdev);
+
+	poll_wait(filp, &exec->poll_wait_queue, wait);
+
+	/*
+	 * Mutex lock protects from two threads from the same application
+	 * calling poll concurrently using the same file handle
+	 */
+	mutex_lock(&client->lock);
+	counter = atomic_read(&client->trigger);
+	if (counter > 0) {
+		/*
+		 * Use atomic here since the trigger may be incremented by
+		 * interrupt handler running concurrently.
+		 */
+		atomic_dec(&client->trigger);
+		ret = POLLIN;
+	}
+	mutex_unlock(&client->lock);
+
+	return ret;
+}
+
+static int client_ioctl_ctx(struct platform_device *pdev,
+			    struct client_ctx *client, void *data)
+{
+	bool acquire_lock = false;
+	struct drm_xocl_ctx *args = data;
+	int ret = 0;
+	int pid = pid_nr(task_tgid(current));
+	struct xocl_dev	*xdev = xocl_get_xdev(pdev);
+	struct exec_core *exec = platform_get_drvdata(pdev);
+	uuid_t *xclbin_id;
+
+	mutex_lock(&client->lock);
+	mutex_lock(&xdev->ctx_list_lock);
+	xclbin_id = (uuid_t *)xocl_icap_get_data(xdev, XCLBIN_UUID);
+	if (!xclbin_id || !uuid_equal(xclbin_id, &args->xclbin_id)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	if (args->cu_index >= XOCL_IP_LAYOUT(xdev)->m_count) {
+		userpf_err(xdev, "cuidx(%d) >= numcus(%d)\n",
+			   args->cu_index, XOCL_IP_LAYOUT(xdev)->m_count);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (args->op == XOCL_CTX_OP_FREE_CTX) {
+		ret = test_and_clear_bit(args->cu_index, client->cu_bitmap) ? 0 : -EINVAL;
+		if (ret) // No context was previously allocated for this CU
+			goto out;
+
+		// CU unlocked explicitly
+		--exec->ip_reference[args->cu_index];
+		if (!--client->num_cus) {
+			// We just gave up the last context, unlock the xclbin
+			ret = xocl_icap_unlock_bitstream(xdev, xclbin_id, pid);
+			client->xclbin_locked = false;
+		}
+		userpf_info(xdev, "CTX del(%pUb, %d, %u)",
+			    xclbin_id, pid, args->cu_index);
+		goto out;
+	}
+
+	if (args->op != XOCL_CTX_OP_ALLOC_CTX) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (args->flags != XOCL_CTX_SHARED) {
+		userpf_err(xdev, "Only shared contexts are supported in this release");
+		ret = -EPERM;
+		goto out;
+	}
+
+	if (!client->num_cus && !client->xclbin_locked)
+		// Process has no other context on any CU yet, hence we need to
+		// lock the xclbin A process uses just one lock for all its ctxs
+		acquire_lock = true;
+
+	if (test_and_set_bit(args->cu_index, client->cu_bitmap)) {
+		userpf_info(xdev, "CTX already allocated by this process");
+		// Context was previously allocated for the same CU,
+		// cannot allocate again
+		ret = 0;
+		goto out;
+	}
+
+	if (acquire_lock) {
+		// This is the first context on any CU for this process,
+		// lock the xclbin
+		ret = xocl_client_lock_bitstream_nolock(xdev, client);
+		if (ret) {
+			// Locking of xclbin failed, give up our context
+			clear_bit(args->cu_index, client->cu_bitmap);
+			goto out;
+		} else {
+			uuid_copy(&client->xclbin_id, xclbin_id);
+		}
+	}
+
+	// Everything is good so far, hence increment the CU reference count
+	++client->num_cus; // explicitly acquired
+	++exec->ip_reference[args->cu_index];
+	xocl_info(&pdev->dev, "CTX add(%pUb, %d, %u, %d)",
+		  xclbin_id, pid, args->cu_index, acquire_lock);
+out:
+	mutex_unlock(&xdev->ctx_list_lock);
+	mutex_unlock(&client->lock);
+	return ret;
+}
+
+static int
+get_bo_paddr(struct xocl_dev *xdev, struct drm_file *filp,
+	     uint32_t bo_hdl, size_t off, size_t size, uint64_t *paddrp)
+{
+	struct drm_device *ddev = filp->minor->dev;
+	struct drm_gem_object *obj;
+	struct drm_xocl_bo *xobj;
+
+	obj = xocl_gem_object_lookup(ddev, filp, bo_hdl);
+	if (!obj) {
+		userpf_err(xdev, "Failed to look up GEM BO 0x%x\n", bo_hdl);
+		return -ENOENT;
+	}
+	xobj = to_xocl_bo(obj);
+
+	if (obj->size <= off || obj->size < off + size || !xobj->mm_node) {
+		userpf_err(xdev, "Failed to get paddr for BO 0x%x\n", bo_hdl);
+//PORT4_20
+//		drm_gem_object_unreference_unlocked(obj);
+		drm_gem_object_put_unlocked(obj);
+		return -EINVAL;
+	}
+
+	*paddrp = xobj->mm_node->start + off;
+//	drm_gem_object_unreference_unlocked(obj);
+	drm_gem_object_put_unlocked(obj);
+	return 0;
+}
+
+static int
+convert_execbuf(struct xocl_dev *xdev, struct drm_file *filp,
+		struct exec_core *exec, struct drm_xocl_bo *xobj)
+{
+	int i;
+	int ret;
+	size_t src_off;
+	size_t dst_off;
+	size_t sz;
+	uint64_t src_addr;
+	uint64_t dst_addr;
+	struct ert_start_copybo_cmd *scmd = (struct ert_start_copybo_cmd *)xobj->vmapping;
+
+	/* Only convert COPYBO cmd for now. */
+	if (scmd->opcode != ERT_START_COPYBO)
+		return 0;
+
+	sz = scmd->size * COPYBO_UNIT;
+
+	src_off = scmd->src_addr_hi;
+	src_off <<= 32;
+	src_off |= scmd->src_addr_lo;
+	ret = get_bo_paddr(xdev, filp, scmd->src_bo_hdl, src_off, sz, &src_addr);
+	if (ret != 0)
+		return ret;
+
+	dst_off = scmd->dst_addr_hi;
+	dst_off <<= 32;
+	dst_off |= scmd->dst_addr_lo;
+	ret = get_bo_paddr(xdev, filp, scmd->dst_bo_hdl, dst_off, sz, &dst_addr);
+	if (ret != 0)
+		return ret;
+
+	ert_fill_copybo_cmd(scmd, 0, 0, src_addr, dst_addr, sz);
+
+	for (i = exec->num_cus - exec->num_cdma; i < exec->num_cus; i++)
+		scmd->cu_mask[i / 32] |= 1 << (i % 32);
+
+	scmd->opcode = ERT_START_CU;
+
+	return 0;
+}
+
+static int
+client_ioctl_execbuf(struct platform_device *pdev,
+		     struct client_ctx *client, void *data, struct drm_file *filp)
+{
+	struct drm_xocl_execbuf *args = data;
+	struct drm_xocl_bo *xobj;
+	struct drm_gem_object *obj;
+	struct drm_xocl_bo *deps[8] = {0};
+	int numdeps = -1;
+	int ret = 0;
+	struct xocl_dev	*xdev = xocl_get_xdev(pdev);
+	struct drm_device *ddev = filp->minor->dev;
+
+	if (xdev->needs_reset) {
+		userpf_err(xdev, "device needs reset, use 'xbutil reset -h'");
+		return -EBUSY;
+	}
+
+	/* Look up the gem object corresponding to the BO handle.
+	 * This adds a reference to the gem object.  The refernece is
+	 * passed to kds or released here if errors occur.
+	 */
+	obj = xocl_gem_object_lookup(ddev, filp, args->exec_bo_handle);
+	if (!obj) {
+		userpf_err(xdev, "Failed to look up GEM BO %d\n",
+		args->exec_bo_handle);
+		return -ENOENT;
+	}
+
+	/* Convert gem object to xocl_bo extension */
+	xobj = to_xocl_bo(obj);
+	if (!xocl_bo_execbuf(xobj) || convert_execbuf(xdev, filp,
+		platform_get_drvdata(pdev), xobj) != 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = validate(pdev, client, xobj);
+	if (ret) {
+		userpf_err(xdev, "Exec buffer validation failed\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Copy dependencies from user.	 It is an error if a BO handle specified
+	 * as a dependency does not exists. Lookup gem object corresponding to bo
+	 * handle.  Convert gem object to xocl_bo extension.  Note that the
+	 * gem lookup acquires a reference to the drm object, this reference
+	 * is passed on to the the scheduler via xocl_exec_add_buffer.
+	 */
+	for (numdeps = 0; numdeps < 8 && args->deps[numdeps]; ++numdeps) {
+		struct drm_gem_object *gobj =
+		  xocl_gem_object_lookup(ddev, filp, args->deps[numdeps]);
+		struct drm_xocl_bo *xbo = gobj ? to_xocl_bo(gobj) : NULL;
+
+		if (!gobj)
+			userpf_err(xdev, "Failed to look up GEM BO %d\n",
+				   args->deps[numdeps]);
+		if (!xbo) {
+			ret = -EINVAL;
+			goto out;
+		}
+		deps[numdeps] = xbo;
+	}
+
+	/* acquire lock on xclbin if necessary */
+	ret = xocl_client_lock_bitstream(xdev, client);
+	if (ret) {
+		userpf_err(xdev, "Failed to lock xclbin\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Add exec buffer to scheduler (kds).	The scheduler manages the
+	 * drm object references acquired by xobj and deps.  It is vital
+	 * that the references are released properly.
+	 */
+	ret = add_exec_buffer(pdev, client, xobj, numdeps, deps);
+	if (ret) {
+		userpf_err(xdev, "Failed to add exec buffer to scheduler\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Return here, noting that the gem objects passed to kds have
+	 * references that must be released by kds itself.  User manages
+	 * a regular reference to all BOs returned as file handles.  These
+	 * references are released with the BOs are freed.
+	 */
+	return ret;
+
+out:
+	for (--numdeps; numdeps >= 0; numdeps--)
+		drm_gem_object_put_unlocked(&deps[numdeps]->base);
+//PORT4_20
+//		drm_gem_object_unreference_unlocked(&deps[numdeps]->base);
+	drm_gem_object_put_unlocked(&xobj->base);
+//	drm_gem_object_unreference_unlocked(&xobj->base);
+	return ret;
+}
+
+int
+client_ioctl(struct platform_device *pdev, int op, void *data, void *drm_filp)
+{
+	struct drm_file *filp = drm_filp;
+	struct client_ctx *client = filp->driver_priv;
+	int ret;
+
+	switch (op) {
+	case DRM_XOCL_CTX:
+		ret = client_ioctl_ctx(pdev, client, data);
+		break;
+	case DRM_XOCL_EXECBUF:
+		ret = client_ioctl_execbuf(pdev, client, data, drm_filp);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+/**
+ * reset() - Reset device exec data structure
+ *
+ * @pdev: platform device to reset
+ *
+ * [Current 2018.3 situation:]
+ * This function is currently called from mgmt icap on every AXI is
+ * freeze/unfreeze.  It ensures that the device exec_core state is reset to
+ * same state as was when scheduler was originally probed for the device.
+ * The callback from icap, ensures that scheduler resets the exec core when
+ * multiple processes are already attached to the device but AXI is reset.
+ *
+ * Even though the very first client created for this device also resets the
+ * exec core, it is possible that further resets are necessary.	 For example
+ * in multi-process case, there can be 'n' processes that attach to the
+ * device.  On first client attach the exec core is reset correctly, but now
+ * assume that 'm' of these processes finishes completely before any remaining
+ * (n-m) processes start using the scheduler.  In this case, the n-m clients have
+ * already been created, but icap resets AXI because the xclbin has no
+ * references (arguably this AXI reset is wrong)
+ *
+ * [Work-in-progress:]
+ * Proper contract:
+ *  Pre-condition: xocl_exec_stop has been called before xocl_exec_reset.
+ *  Pre-condition: new bitstream has been downloaded and AXI has been reset
+ */
+static int
+reset(struct platform_device *pdev)
+{
+	struct exec_core *exec = platform_get_drvdata(pdev);
+
+	exec_stop(exec);   // remove when upstream explicitly calls stop()
+	exec_reset(exec);
+	return 0;
+}
+
+/**
+ * stop() - Reset device exec data structure
+ *
+ * This API must be called prior to performing an AXI reset and downloading of
+ * a new xclbin.  Calling this API flushes the commands running on current
+ * device and prevents new commands from being scheduled on the device.	 This
+ * effectively prevents 'xbutil top' from issuing CU_STAT commands while
+ * programming is performed.
+ *
+ * Pre-condition: xocl_client_release has been called, e.g there are no
+ *		  current clients using the bitstream
+ */
+static int
+stop(struct platform_device *pdev)
+{
+	struct exec_core *exec = platform_get_drvdata(pdev);
+
+	exec_stop(exec);
+	return 0;
+}
+
+/**
+ * validate() - Check if requested cmd is valid in the current context
+ */
+static int
+validate(struct platform_device *pdev, struct client_ctx *client, const struct drm_xocl_bo *bo)
+{
+	struct ert_packet *ecmd = (struct ert_packet *)bo->vmapping;
+	struct ert_start_kernel_cmd *scmd = (struct ert_start_kernel_cmd *)bo->vmapping;
+	unsigned int i = 0;
+	u32 ctx_cus[4] = {0};
+	u32 cumasks = 0;
+	int err = 0;
+
+	SCHED_DEBUGF("-> %s(%d)\n", __func__, ecmd->opcode);
+
+	/* cus for start kernel commands only */
+	if (ecmd->opcode != ERT_START_CU)
+		return 0; /* ok */
+
+	/* client context cu bitmap may not change while validating */
+	mutex_lock(&client->lock);
+
+	/* no specific CUs selected, maybe ctx is not used by client */
+	if (bitmap_empty(client->cu_bitmap, MAX_CUS)) {
+		userpf_err(xocl_get_xdev(pdev), "%s found no CUs in ctx\n", __func__);
+		goto out; /* ok */
+	}
+
+	/* Check CUs in cmd BO against CUs in context */
+	cumasks = 1 + scmd->extra_cu_masks;
+	xocl_bitmap_to_arr32(ctx_cus, client->cu_bitmap, cumasks * 32);
+
+	for (i = 0; i < cumasks; ++i) {
+		uint32_t cmd_cus = ecmd->data[i];
+		/* cmd_cus must be subset of ctx_cus */
+		if (cmd_cus & ~ctx_cus[i]) {
+			SCHED_DEBUGF("<- %s(1), CU mismatch in mask(%d) cmd(0x%x) ctx(0x%x)\n",
+				     __func__, i, cmd_cus, ctx_cus[i]);
+			err = 1;
+			goto out; /* error */
+		}
+	}
+
+
+out:
+	mutex_unlock(&client->lock);
+	SCHED_DEBUGF("<- %s(%d) cmd and ctx CUs match\n", __func__, err);
+	return err;
+
+}
+
+struct xocl_mb_scheduler_funcs sche_ops = {
+	.create_client = create_client,
+	.destroy_client = destroy_client,
+	.poll_client = poll_client,
+	.client_ioctl = client_ioctl,
+	.stop = stop,
+	.reset = reset,
+};
+
+/* sysfs */
+static ssize_t
+kds_numcus_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct exec_core *exec = dev_get_exec(dev);
+	unsigned int cus = exec ? exec->num_cus - exec->num_cdma : 0;
+
+	return sprintf(buf, "%d\n", cus);
+}
+static DEVICE_ATTR_RO(kds_numcus);
+
+static ssize_t
+kds_numcdmas_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct xocl_dev *xdev = dev_get_xdev(dev);
+	uint32_t *cdma = xocl_cdma_addr(xdev);
+	unsigned int cdmas = cdma ? 1 : 0; //TBD
+
+	return sprintf(buf, "%d\n", cdmas);
+}
+static DEVICE_ATTR_RO(kds_numcdmas);
+
+static ssize_t
+kds_custat_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct exec_core *exec = dev_get_exec(dev);
+	struct xocl_dev *xdev = exec_get_xdev(exec);
+	struct client_ctx client;
+	struct ert_packet packet;
+	unsigned int count = 0;
+	ssize_t sz = 0;
+
+	// minimum required initialization of client
+	client.abort = false;
+	client.xdev = xdev;
+	atomic_set(&client.trigger, 0);
+	atomic_set(&client.outstanding_execs, 0);
+
+	packet.opcode = ERT_CU_STAT;
+	packet.type = ERT_CTRL;
+	packet.count = 1;  // data[1]
+
+	if (add_ctrl_cmd(exec, &client, &packet) == 0) {
+		int retry = 5;
+
+		SCHED_DEBUGF("-> custat waiting for command to finish\n");
+		// wait for command completion
+		while (--retry && atomic_read(&client.outstanding_execs))
+			msleep(100);
+		if (retry == 0 && atomic_read(&client.outstanding_execs))
+			userpf_info(xdev, "custat unexpected timeout\n");
+		SCHED_DEBUGF("<- custat retry(%d)\n", retry);
+	}
+
+	for (count = 0; count < exec->num_cus; ++count)
+		sz += sprintf(buf+sz, "CU[@0x%x] : %d\n",
+			      exec_cu_base_addr(exec, count),
+			      exec_cu_usage(exec, count));
+	if (sz)
+		buf[sz++] = 0;
+
+	return sz;
+}
+static DEVICE_ATTR_RO(kds_custat);
+
+static struct attribute *kds_sysfs_attrs[] = {
+	&dev_attr_kds_numcus.attr,
+	&dev_attr_kds_numcdmas.attr,
+	&dev_attr_kds_custat.attr,
+	NULL
+};
+
+static const struct attribute_group kds_sysfs_attr_group = {
+	.attrs = kds_sysfs_attrs,
+};
+
+static void
+user_sysfs_destroy_kds(struct platform_device *pdev)
+{
+	sysfs_remove_group(&pdev->dev.kobj, &kds_sysfs_attr_group);
+}
+
+static int
+user_sysfs_create_kds(struct platform_device *pdev)
+{
+	int err = sysfs_create_group(&pdev->dev.kobj, &kds_sysfs_attr_group);
+
+	if (err)
+		userpf_err(xocl_get_xdev(pdev), "create kds attr failed: 0x%x", err);
+	return err;
+}
+
+/**
+ * Init scheduler
+ */
+static int mb_scheduler_probe(struct platform_device *pdev)
+{
+	struct exec_core *exec = exec_create(pdev, &scheduler0);
+
+	if (!exec)
+		return -ENOMEM;
+
+	if (user_sysfs_create_kds(pdev))
+		goto err;
+
+	init_scheduler_thread(&scheduler0);
+	xocl_subdev_register(pdev, XOCL_SUBDEV_MB_SCHEDULER, &sche_ops);
+	platform_set_drvdata(pdev, exec);
+
+	DRM_INFO("command scheduler started\n");
+
+	return 0;
+
+err:
+	devm_kfree(&pdev->dev, exec);
+	return 1;
+}
+
+/**
+ * Fini scheduler
+ */
+static int mb_scheduler_remove(struct platform_device *pdev)
+{
+	struct xocl_dev *xdev;
+	int i;
+	struct exec_core *exec = platform_get_drvdata(pdev);
+
+	SCHED_DEBUGF("-> %s\n", __func__);
+	fini_scheduler_thread(exec_scheduler(exec));
+
+	xdev = xocl_get_xdev(pdev);
+	for (i = 0; i < exec->intr_num; i++) {
+		xocl_user_interrupt_config(xdev, i + exec->intr_base, false);
+		xocl_user_interrupt_reg(xdev, i + exec->intr_base,
+			NULL, NULL);
+	}
+	mutex_destroy(&exec->exec_lock);
+
+	user_sysfs_destroy_kds(pdev);
+	exec_destroy(exec);
+	platform_set_drvdata(pdev, NULL);
+
+	SCHED_DEBUGF("<- %s\n", __func__);
+	DRM_INFO("command scheduler removed\n");
+	return 0;
+}
+
+static struct platform_device_id mb_sche_id_table[] = {
+	{ XOCL_MB_SCHEDULER, 0 },
+	{ },
+};
+
+static struct platform_driver	mb_scheduler_driver = {
+	.probe		= mb_scheduler_probe,
+	.remove		= mb_scheduler_remove,
+	.driver		= {
+		.name = "xocl_mb_sche",
+	},
+	.id_table	= mb_sche_id_table,
+};
+
+int __init xocl_init_mb_scheduler(void)
+{
+	return platform_driver_register(&mb_scheduler_driver);
+}
+
+void xocl_fini_mb_scheduler(void)
+{
+	SCHED_DEBUGF("-> %s\n", __func__);
+	platform_driver_unregister(&mb_scheduler_driver);
+	SCHED_DEBUGF("<- %s\n", __func__);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/microblaze.c b/drivers/gpu/drm/xocl/subdev/microblaze.c
new file mode 100644
index 000000000000..38cfbdbb39ef
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/microblaze.c
@@ -0,0 +1,722 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2016-2019 Xilinx, Inc. All rights reserved.
+ *
+ * Authors: Lizhi.HOu@xilinx.com
+ *
+ */
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/vmalloc.h>
+#include "../xocl_drv.h"
+#include <drm/xmgmt_drm.h>
+
+#define MAX_RETRY	50
+#define RETRY_INTERVAL	100	  //ms
+
+#define	MAX_IMAGE_LEN	0x20000
+
+#define	REG_VERSION		0
+#define	REG_ID			0x4
+#define	REG_STATUS		0x8
+#define	REG_ERR			0xC
+#define	REG_CAP			0x10
+#define	REG_CTL			0x18
+#define	REG_STOP_CONFIRM	0x1C
+#define	REG_CURR_BASE		0x20
+#define	REG_POWER_CHECKSUM	0x1A4
+
+#define	VALID_ID		0x74736574
+
+#define	GPIO_RESET		0x0
+#define	GPIO_ENABLED		0x1
+
+#define	SELF_JUMP(ins)		(((ins) & 0xfc00ffff) == 0xb8000000)
+
+enum ctl_mask {
+	CTL_MASK_CLEAR_POW	= 0x1,
+	CTL_MASK_CLEAR_ERR	= 0x2,
+	CTL_MASK_PAUSE		= 0x4,
+	CTL_MASK_STOP		= 0x8,
+};
+
+enum status_mask {
+	STATUS_MASK_INIT_DONE		= 0x1,
+	STATUS_MASK_STOPPED		= 0x2,
+	STATUS_MASK_PAUSE		= 0x4,
+};
+
+enum cap_mask {
+	CAP_MASK_PM			= 0x1,
+};
+
+enum {
+	MB_STATE_INIT = 0,
+	MB_STATE_RUN,
+	MB_STATE_RESET,
+};
+
+enum {
+	IO_REG,
+	IO_GPIO,
+	IO_IMAGE_MGMT,
+	IO_IMAGE_SCHE,
+	NUM_IOADDR
+};
+
+#define	READ_REG32(mb, off)		\
+	XOCL_READ_REG32(mb->base_addrs[IO_REG] + off)
+#define	WRITE_REG32(mb, val, off)	\
+	XOCL_WRITE_REG32(val, mb->base_addrs[IO_REG] + off)
+
+#define	READ_GPIO(mb, off)		\
+	XOCL_READ_REG32(mb->base_addrs[IO_GPIO] + off)
+#define	WRITE_GPIO(mb, val, off)	\
+	XOCL_WRITE_REG32(val, mb->base_addrs[IO_GPIO] + off)
+
+#define	READ_IMAGE_MGMT(mb, off)		\
+	XOCL_READ_REG32(mb->base_addrs[IO_IMAGE_MGMT] + off)
+
+#define	COPY_MGMT(mb, buf, len)		\
+	xocl_memcpy_toio(mb->base_addrs[IO_IMAGE_MGMT], buf, len)
+#define	COPY_SCHE(mb, buf, len)		\
+	xocl_memcpy_toio(mb->base_addrs[IO_IMAGE_SCHE], buf, len)
+
+struct xocl_mb {
+	struct platform_device	*pdev;
+	void __iomem		*base_addrs[NUM_IOADDR];
+
+	struct device		*hwmon_dev;
+	bool			enabled;
+	u32			state;
+	u32			cap;
+	struct mutex		mb_lock;
+
+	char			*sche_binary;
+	u32			sche_binary_length;
+	char			*mgmt_binary;
+	u32			mgmt_binary_length;
+};
+
+static int mb_stop(struct xocl_mb *mb);
+static int mb_start(struct xocl_mb *mb);
+
+/* sysfs support */
+static void safe_read32(struct xocl_mb *mb, u32 reg, u32 *val)
+{
+	mutex_lock(&mb->mb_lock);
+	if (mb->enabled && mb->state == MB_STATE_RUN)
+		*val = READ_REG32(mb, reg);
+	else
+		*val = 0;
+	mutex_unlock(&mb->mb_lock);
+}
+
+static void safe_write32(struct xocl_mb *mb, u32 reg, u32 val)
+{
+	mutex_lock(&mb->mb_lock);
+	if (mb->enabled && mb->state == MB_STATE_RUN)
+		WRITE_REG32(mb, val, reg);
+	mutex_unlock(&mb->mb_lock);
+}
+
+static ssize_t version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_mb *mb = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(mb, REG_VERSION, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(version);
+
+static ssize_t id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_mb *mb = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(mb, REG_ID, &val);
+
+	return sprintf(buf, "%x\n", val);
+}
+static DEVICE_ATTR_RO(id);
+
+static ssize_t status_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_mb *mb = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(mb, REG_STATUS, &val);
+
+	return sprintf(buf, "%x\n", val);
+}
+static DEVICE_ATTR_RO(status);
+
+static ssize_t error_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_mb *mb = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(mb, REG_ERR, &val);
+
+	return sprintf(buf, "%x\n", val);
+}
+static DEVICE_ATTR_RO(error);
+
+static ssize_t capability_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_mb *mb = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(mb, REG_CAP, &val);
+
+	return sprintf(buf, "%x\n", val);
+}
+static DEVICE_ATTR_RO(capability);
+
+static ssize_t power_checksum_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_mb *mb = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(mb, REG_POWER_CHECKSUM, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(power_checksum);
+
+static ssize_t pause_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_mb *mb = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(mb, REG_CTL, &val);
+
+	return sprintf(buf, "%d\n", !!(val & CTL_MASK_PAUSE));
+}
+
+static ssize_t pause_store(struct device *dev,
+	struct device_attribute *da, const char *buf, size_t count)
+{
+	struct xocl_mb *mb = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	if (kstrtou32(buf, 10, &val) == -EINVAL || val > 1)
+		return -EINVAL;
+
+	val = val ? CTL_MASK_PAUSE : 0;
+	safe_write32(mb, REG_CTL, val);
+
+	return count;
+}
+static DEVICE_ATTR_RW(pause);
+
+static ssize_t reset_store(struct device *dev,
+	struct device_attribute *da, const char *buf, size_t count)
+{
+	struct xocl_mb *mb = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	if (kstrtou32(buf, 10, &val) == -EINVAL || val > 1)
+		return -EINVAL;
+
+	if (val) {
+		mb_stop(mb);
+		mb_start(mb);
+	}
+
+	return count;
+}
+static DEVICE_ATTR_WO(reset);
+
+static struct attribute *mb_attrs[] = {
+	&dev_attr_version.attr,
+	&dev_attr_id.attr,
+	&dev_attr_status.attr,
+	&dev_attr_error.attr,
+	&dev_attr_capability.attr,
+	&dev_attr_power_checksum.attr,
+	&dev_attr_pause.attr,
+	&dev_attr_reset.attr,
+	NULL,
+};
+static struct attribute_group mb_attr_group = {
+	.attrs = mb_attrs,
+};
+
+static ssize_t show_mb_pw(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct xocl_mb *mb = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(mb, REG_CURR_BASE + attr->index * sizeof(u32), &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+
+static SENSOR_DEVICE_ATTR(curr1_highest, 0444, show_mb_pw, NULL, 0);
+static SENSOR_DEVICE_ATTR(curr1_average, 0444, show_mb_pw, NULL, 1);
+static SENSOR_DEVICE_ATTR(curr1_input, 0444, show_mb_pw, NULL, 2);
+static SENSOR_DEVICE_ATTR(curr2_highest, 0444, show_mb_pw, NULL, 3);
+static SENSOR_DEVICE_ATTR(curr2_average, 0444, show_mb_pw, NULL, 4);
+static SENSOR_DEVICE_ATTR(curr2_input, 0444, show_mb_pw, NULL, 5);
+static SENSOR_DEVICE_ATTR(curr3_highest, 0444, show_mb_pw, NULL, 6);
+static SENSOR_DEVICE_ATTR(curr3_average, 0444, show_mb_pw, NULL, 7);
+static SENSOR_DEVICE_ATTR(curr3_input, 0444, show_mb_pw, NULL, 8);
+static SENSOR_DEVICE_ATTR(curr4_highest, 0444, show_mb_pw, NULL, 9);
+static SENSOR_DEVICE_ATTR(curr4_average, 0444, show_mb_pw, NULL, 10);
+static SENSOR_DEVICE_ATTR(curr4_input, 0444, show_mb_pw, NULL, 11);
+static SENSOR_DEVICE_ATTR(curr5_highest, 0444, show_mb_pw, NULL, 12);
+static SENSOR_DEVICE_ATTR(curr5_average, 0444, show_mb_pw, NULL, 13);
+static SENSOR_DEVICE_ATTR(curr5_input, 0444, show_mb_pw, NULL, 14);
+static SENSOR_DEVICE_ATTR(curr6_highest, 0444, show_mb_pw, NULL, 15);
+static SENSOR_DEVICE_ATTR(curr6_average, 0444, show_mb_pw, NULL, 16);
+static SENSOR_DEVICE_ATTR(curr6_input, 0444, show_mb_pw, NULL, 17);
+
+static struct attribute *hwmon_mb_attributes[] = {
+	&sensor_dev_attr_curr1_highest.dev_attr.attr,
+	&sensor_dev_attr_curr1_average.dev_attr.attr,
+	&sensor_dev_attr_curr1_input.dev_attr.attr,
+	&sensor_dev_attr_curr2_highest.dev_attr.attr,
+	&sensor_dev_attr_curr2_average.dev_attr.attr,
+	&sensor_dev_attr_curr2_input.dev_attr.attr,
+	&sensor_dev_attr_curr3_highest.dev_attr.attr,
+	&sensor_dev_attr_curr3_average.dev_attr.attr,
+	&sensor_dev_attr_curr3_input.dev_attr.attr,
+	&sensor_dev_attr_curr4_highest.dev_attr.attr,
+	&sensor_dev_attr_curr4_average.dev_attr.attr,
+	&sensor_dev_attr_curr4_input.dev_attr.attr,
+	&sensor_dev_attr_curr5_highest.dev_attr.attr,
+	&sensor_dev_attr_curr5_average.dev_attr.attr,
+	&sensor_dev_attr_curr5_input.dev_attr.attr,
+	&sensor_dev_attr_curr6_highest.dev_attr.attr,
+	&sensor_dev_attr_curr6_average.dev_attr.attr,
+	&sensor_dev_attr_curr6_input.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group hwmon_mb_attrgroup = {
+	.attrs = hwmon_mb_attributes,
+};
+
+static ssize_t show_name(struct device *dev, struct device_attribute *da,
+			 char *buf)
+{
+	return sprintf(buf, "%s\n", XCLMGMT_MB_HWMON_NAME);
+}
+
+static struct sensor_device_attribute name_attr =
+	SENSOR_ATTR(name, 0444, show_name, NULL, 0);
+
+static void mgmt_sysfs_destroy_mb(struct platform_device *pdev)
+{
+	struct xocl_mb *mb;
+
+	mb = platform_get_drvdata(pdev);
+
+	if (!mb->enabled)
+		return;
+
+	if (mb->hwmon_dev) {
+		device_remove_file(mb->hwmon_dev, &name_attr.dev_attr);
+		sysfs_remove_group(&mb->hwmon_dev->kobj,
+			&hwmon_mb_attrgroup);
+		hwmon_device_unregister(mb->hwmon_dev);
+		mb->hwmon_dev = NULL;
+	}
+
+	sysfs_remove_group(&pdev->dev.kobj, &mb_attr_group);
+}
+
+static int mgmt_sysfs_create_mb(struct platform_device *pdev)
+{
+	struct xocl_mb *mb;
+	struct xocl_dev_core *core;
+	int err;
+
+	mb = platform_get_drvdata(pdev);
+	core = XDEV(xocl_get_xdev(pdev));
+
+	if (!mb->enabled)
+		return 0;
+	err = sysfs_create_group(&pdev->dev.kobj, &mb_attr_group);
+	if (err) {
+		xocl_err(&pdev->dev, "create mb attrs failed: 0x%x", err);
+		goto create_attr_failed;
+	}
+	mb->hwmon_dev = hwmon_device_register(&core->pdev->dev);
+	if (IS_ERR(mb->hwmon_dev)) {
+		err = PTR_ERR(mb->hwmon_dev);
+		xocl_err(&pdev->dev, "register mb hwmon failed: 0x%x", err);
+		goto hwmon_reg_failed;
+	}
+
+	dev_set_drvdata(mb->hwmon_dev, mb);
+
+	err = device_create_file(mb->hwmon_dev, &name_attr.dev_attr);
+	if (err) {
+		xocl_err(&pdev->dev, "create attr name failed: 0x%x", err);
+		goto create_name_failed;
+	}
+
+	err = sysfs_create_group(&mb->hwmon_dev->kobj,
+		&hwmon_mb_attrgroup);
+	if (err) {
+		xocl_err(&pdev->dev, "create pw group failed: 0x%x", err);
+		goto create_pw_failed;
+	}
+
+	return 0;
+
+create_pw_failed:
+	device_remove_file(mb->hwmon_dev, &name_attr.dev_attr);
+create_name_failed:
+	hwmon_device_unregister(mb->hwmon_dev);
+	mb->hwmon_dev = NULL;
+hwmon_reg_failed:
+	sysfs_remove_group(&pdev->dev.kobj, &mb_attr_group);
+create_attr_failed:
+	return err;
+}
+
+static int mb_stop(struct xocl_mb *mb)
+{
+	int retry = 0;
+	int ret = 0;
+	u32 reg_val = 0;
+
+	if (!mb->enabled)
+		return 0;
+
+	mutex_lock(&mb->mb_lock);
+	reg_val = READ_GPIO(mb, 0);
+	xocl_info(&mb->pdev->dev, "Reset GPIO 0x%x", reg_val);
+	if (reg_val == GPIO_RESET) {
+		/* MB in reset status */
+		mb->state = MB_STATE_RESET;
+		goto out;
+	}
+
+	xocl_info(&mb->pdev->dev,
+		"MGMT Image magic word, 0x%x, status 0x%x, id 0x%x",
+		READ_IMAGE_MGMT(mb, 0),
+		READ_REG32(mb, REG_STATUS),
+		READ_REG32(mb, REG_ID));
+
+	if (!SELF_JUMP(READ_IMAGE_MGMT(mb, 0))) {
+		/* non cold boot */
+		reg_val = READ_REG32(mb, REG_STATUS);
+		if (!(reg_val & STATUS_MASK_STOPPED)) {
+			// need to stop microblaze
+			xocl_info(&mb->pdev->dev, "stopping microblaze...");
+			WRITE_REG32(mb, CTL_MASK_STOP, REG_CTL);
+			WRITE_REG32(mb, 1, REG_STOP_CONFIRM);
+			while (retry++ < MAX_RETRY &&
+				!(READ_REG32(mb, REG_STATUS) &
+				STATUS_MASK_STOPPED)) {
+				msleep(RETRY_INTERVAL);
+			}
+			if (retry >= MAX_RETRY) {
+				xocl_err(&mb->pdev->dev,
+					"Failed to stop microblaze");
+				xocl_err(&mb->pdev->dev,
+					"Error Reg 0x%x",
+					READ_REG32(mb, REG_ERR));
+				ret = -EIO;
+				goto out;
+			}
+		}
+		xocl_info(&mb->pdev->dev, "Microblaze Stopped, retry %d",
+			retry);
+	}
+
+	/* hold reset */
+	WRITE_GPIO(mb, GPIO_RESET, 0);
+	mb->state = MB_STATE_RESET;
+out:
+	mutex_unlock(&mb->mb_lock);
+
+	return ret;
+}
+
+static int mb_start(struct xocl_mb *mb)
+{
+	int retry = 0;
+	u32 reg_val = 0;
+	int ret = 0;
+	void *xdev_hdl;
+
+	if (!mb->enabled)
+		return 0;
+
+	xdev_hdl = xocl_get_xdev(mb->pdev);
+
+	mutex_lock(&mb->mb_lock);
+	reg_val = READ_GPIO(mb, 0);
+	xocl_info(&mb->pdev->dev, "Reset GPIO 0x%x", reg_val);
+	if (reg_val == GPIO_ENABLED)
+		goto out;
+
+	xocl_info(&mb->pdev->dev, "Start Microblaze...");
+	xocl_info(&mb->pdev->dev, "MGMT Image magic word, 0x%x",
+		READ_IMAGE_MGMT(mb, 0));
+
+	if (xocl_mb_mgmt_on(xdev_hdl)) {
+		xocl_info(&mb->pdev->dev, "Copying mgmt image len %d",
+			mb->mgmt_binary_length);
+		COPY_MGMT(mb, mb->mgmt_binary, mb->mgmt_binary_length);
+	}
+
+	if (xocl_mb_sched_on(xdev_hdl)) {
+		xocl_info(&mb->pdev->dev, "Copying scheduler image len %d",
+			mb->sche_binary_length);
+		COPY_SCHE(mb, mb->sche_binary, mb->sche_binary_length);
+	}
+
+	WRITE_GPIO(mb, GPIO_ENABLED, 0);
+	xocl_info(&mb->pdev->dev,
+		"MGMT Image magic word, 0x%x, status 0x%x, id 0x%x",
+		READ_IMAGE_MGMT(mb, 0),
+		READ_REG32(mb, REG_STATUS),
+		READ_REG32(mb, REG_ID));
+	do {
+		msleep(RETRY_INTERVAL);
+	} while (retry++ < MAX_RETRY && (READ_REG32(mb, REG_STATUS) &
+		STATUS_MASK_STOPPED));
+
+	/* Extra pulse needed as workaround for axi interconnect issue in DSA */
+	if (retry >= MAX_RETRY) {
+		retry = 0;
+		WRITE_GPIO(mb, GPIO_RESET, 0);
+		WRITE_GPIO(mb, GPIO_ENABLED, 0);
+		do {
+			msleep(RETRY_INTERVAL);
+		} while (retry++ < MAX_RETRY && (READ_REG32(mb, REG_STATUS) &
+			STATUS_MASK_STOPPED));
+	}
+
+	if (retry >= MAX_RETRY) {
+		xocl_err(&mb->pdev->dev, "Failed to start microblaze");
+		xocl_err(&mb->pdev->dev, "Error Reg 0x%x",
+				READ_REG32(mb, REG_ERR));
+			ret = -EIO;
+	}
+
+	mb->cap = READ_REG32(mb, REG_CAP);
+	mb->state = MB_STATE_RUN;
+out:
+	mutex_unlock(&mb->mb_lock);
+
+	return ret;
+}
+
+static void mb_reset(struct platform_device *pdev)
+{
+	struct xocl_mb *mb;
+
+	xocl_info(&pdev->dev, "Reset Microblaze...");
+	mb = platform_get_drvdata(pdev);
+	if (!mb)
+		return;
+
+	mb_stop(mb);
+	mb_start(mb);
+}
+
+static int load_mgmt_image(struct platform_device *pdev, const char *image,
+	u32 len)
+{
+	struct xocl_mb *mb;
+	char *binary;
+
+	if (len > MAX_IMAGE_LEN)
+		return -EINVAL;
+
+	mb = platform_get_drvdata(pdev);
+	if (!mb)
+		return -EINVAL;
+
+	binary = mb->mgmt_binary;
+	mb->mgmt_binary = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+	if (!mb->mgmt_binary)
+		return -ENOMEM;
+
+	if (binary)
+		devm_kfree(&pdev->dev, binary);
+	memcpy(mb->mgmt_binary, image, len);
+	mb->mgmt_binary_length = len;
+
+	return 0;
+}
+
+static int load_sche_image(struct platform_device *pdev, const char *image,
+	u32 len)
+{
+	struct xocl_mb *mb;
+	char *binary = NULL;
+
+	if (len > MAX_IMAGE_LEN)
+		return -EINVAL;
+
+	mb = platform_get_drvdata(pdev);
+	if (!mb)
+		return -EINVAL;
+
+	binary = mb->sche_binary;
+	mb->sche_binary = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+	if (!mb->sche_binary)
+		return -ENOMEM;
+
+	if (binary)
+		devm_kfree(&pdev->dev, binary);
+	memcpy(mb->sche_binary, image, len);
+	mb->sche_binary_length = len;
+
+	return 0;
+}
+
+//Have a function stub but don't actually do anything when this is called
+static int mb_ignore(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct xocl_mb_funcs mb_ops = {
+	.load_mgmt_image	= load_mgmt_image,
+	.load_sche_image	= load_sche_image,
+	.reset			= mb_reset,
+	.stop			= mb_ignore,
+};
+
+
+
+static int mb_remove(struct platform_device *pdev)
+{
+	struct xocl_mb *mb;
+	int	i;
+
+	mb = platform_get_drvdata(pdev);
+	if (!mb)
+		return 0;
+
+	if (mb->mgmt_binary)
+		devm_kfree(&pdev->dev, mb->mgmt_binary);
+	if (mb->sche_binary)
+		devm_kfree(&pdev->dev, mb->sche_binary);
+
+	/*
+	 * It is more secure that MB keeps running even driver is unloaded.
+	 * Even user unload our driver and use their own stuff, MB will still
+	 * be able to monitor the board unless user stops it explicitly
+	 */
+	mb_stop(mb);
+
+	mgmt_sysfs_destroy_mb(pdev);
+
+	for (i = 0; i < NUM_IOADDR; i++) {
+		if (mb->base_addrs[i])
+			iounmap(mb->base_addrs[i]);
+	}
+
+	mutex_destroy(&mb->mb_lock);
+
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(&pdev->dev, mb);
+
+	return 0;
+}
+
+static int mb_probe(struct platform_device *pdev)
+{
+	struct xocl_mb *mb;
+	struct resource *res;
+	void	*xdev_hdl;
+	int i, err;
+
+	mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL);
+	if (!mb) {
+		xocl_err(&pdev->dev, "out of memory");
+		return -ENOMEM;
+	}
+
+	mb->pdev = pdev;
+	platform_set_drvdata(pdev, mb);
+
+	xdev_hdl = xocl_get_xdev(pdev);
+	if (xocl_mb_mgmt_on(xdev_hdl) || xocl_mb_sched_on(xdev_hdl)) {
+		xocl_info(&pdev->dev, "Microblaze is supported.");
+		mb->enabled = true;
+	} else {
+		xocl_info(&pdev->dev, "Microblaze is not supported.");
+		devm_kfree(&pdev->dev, mb);
+		platform_set_drvdata(pdev, NULL);
+		return 0;
+	}
+
+	for (i = 0; i < NUM_IOADDR; i++) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		xocl_info(&pdev->dev, "IO start: 0x%llx, end: 0x%llx",
+			res->start, res->end);
+		mb->base_addrs[i] =
+			ioremap_nocache(res->start, res->end - res->start + 1);
+		if (!mb->base_addrs[i]) {
+			err = -EIO;
+			xocl_err(&pdev->dev, "Map iomem failed");
+			goto failed;
+		}
+	}
+
+	err = mgmt_sysfs_create_mb(pdev);
+	if (err) {
+		xocl_err(&pdev->dev, "Create sysfs failed, err %d", err);
+		goto failed;
+	}
+
+	xocl_subdev_register(pdev, XOCL_SUBDEV_MB, &mb_ops);
+
+	mutex_init(&mb->mb_lock);
+
+	return 0;
+
+failed:
+	mb_remove(pdev);
+	return err;
+}
+
+struct platform_device_id mb_id_table[] = {
+	{ XOCL_MB, 0 },
+	{ },
+};
+
+static struct platform_driver	mb_driver = {
+	.probe		= mb_probe,
+	.remove		= mb_remove,
+	.driver		= {
+		.name = "xocl_mb",
+	},
+	.id_table = mb_id_table,
+};
+
+int __init xocl_init_mb(void)
+{
+	return platform_driver_register(&mb_driver);
+}
+
+void xocl_fini_mb(void)
+{
+	platform_driver_unregister(&mb_driver);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/mig.c b/drivers/gpu/drm/xocl/subdev/mig.c
new file mode 100644
index 000000000000..5a574f7af796
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/mig.c
@@ -0,0 +1,256 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2018-2019 Xilinx, Inc. All rights reserved.
+ *
+ * Authors: Chien-Wei Lan <chienwei@xilinx.com>
+ *
+ */
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include "../xocl_drv.h"
+#include <drm/xmgmt_drm.h>
+
+/* Registers are defined in pg150-ultrascale-memory-ip.pdf:
+ * AXI4-Lite Slave Control/Status Register Map
+ */
+
+#define MIG_DEBUG
+#define	MIG_DEV2MIG(dev)	\
+	((struct xocl_mig *)platform_get_drvdata(to_platform_device(dev)))
+#define	MIG_DEV2BASE(dev)	(MIG_DEV2MIG(dev)->base)
+
+#define ECC_STATUS	0x0
+#define ECC_ON_OFF	0x8
+#define CE_CNT		0xC
+#define CE_ADDR_LO	0x1C0
+#define CE_ADDR_HI	0x1C4
+#define UE_ADDR_LO	0x2C0
+#define UE_ADDR_HI	0x2C4
+#define INJ_FAULT_REG	0x300
+
+struct xocl_mig {
+	void __iomem	*base;
+	struct device	*mig_dev;
+};
+
+static ssize_t ecc_ue_ffa_show(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	uint64_t val = ioread32(MIG_DEV2BASE(dev) + UE_ADDR_HI);
+
+	val <<= 32;
+	val |= ioread32(MIG_DEV2BASE(dev) + UE_ADDR_LO);
+	return sprintf(buf, "0x%llx\n", val);
+}
+static DEVICE_ATTR_RO(ecc_ue_ffa);
+
+
+static ssize_t ecc_ce_ffa_show(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	uint64_t val = ioread32(MIG_DEV2BASE(dev) + CE_ADDR_HI);
+
+	val <<= 32;
+	val |= ioread32(MIG_DEV2BASE(dev) + CE_ADDR_LO);
+	return sprintf(buf, "0x%llx\n", val);
+}
+static DEVICE_ATTR_RO(ecc_ce_ffa);
+
+
+static ssize_t ecc_ce_cnt_show(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	return sprintf(buf, "%u\n", ioread32(MIG_DEV2BASE(dev) + CE_CNT));
+}
+static DEVICE_ATTR_RO(ecc_ce_cnt);
+
+
+static ssize_t ecc_status_show(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	return sprintf(buf, "%u\n", ioread32(MIG_DEV2BASE(dev) + ECC_STATUS));
+}
+static DEVICE_ATTR_RO(ecc_status);
+
+
+static ssize_t ecc_reset_store(struct device *dev, struct device_attribute *da,
+	const char *buf, size_t count)
+{
+	iowrite32(0x3, MIG_DEV2BASE(dev) + ECC_STATUS);
+	iowrite32(0, MIG_DEV2BASE(dev) + CE_CNT);
+	return count;
+}
+static DEVICE_ATTR_WO(ecc_reset);
+
+
+static ssize_t ecc_enabled_show(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	return sprintf(buf, "%u\n", ioread32(MIG_DEV2BASE(dev) + ECC_ON_OFF));
+}
+static ssize_t ecc_enabled_store(struct device *dev,
+	struct device_attribute *da, const char *buf, size_t count)
+{
+	uint32_t val;
+
+	if (sscanf(buf, "%d", &val) != 1 || val > 1) {
+		xocl_err(&to_platform_device(dev)->dev,
+			"usage: echo [0|1] > ecc_enabled");
+		return -EINVAL;
+	}
+
+	iowrite32(val, MIG_DEV2BASE(dev) + ECC_ON_OFF);
+	return count;
+}
+static DEVICE_ATTR_RW(ecc_enabled);
+
+
+#ifdef MIG_DEBUG
+static ssize_t ecc_inject_store(struct device *dev, struct device_attribute *da,
+	const char *buf, size_t count)
+{
+	iowrite32(1, MIG_DEV2BASE(dev) + INJ_FAULT_REG);
+	return count;
+}
+static DEVICE_ATTR_WO(ecc_inject);
+#endif
+
+
+/* Standard sysfs entry for all dynamic subdevices. */
+static ssize_t name_show(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	return sprintf(buf, "%s\n", XOCL_GET_SUBDEV_PRIV(dev));
+}
+static DEVICE_ATTR_RO(name);
+
+
+static struct attribute *mig_attributes[] = {
+	&dev_attr_name.attr,
+	&dev_attr_ecc_enabled.attr,
+	&dev_attr_ecc_status.attr,
+	&dev_attr_ecc_ce_cnt.attr,
+	&dev_attr_ecc_ce_ffa.attr,
+	&dev_attr_ecc_ue_ffa.attr,
+	&dev_attr_ecc_reset.attr,
+#ifdef MIG_DEBUG
+	&dev_attr_ecc_inject.attr,
+#endif
+	NULL
+};
+
+static const struct attribute_group mig_attrgroup = {
+	.attrs = mig_attributes,
+};
+
+static void mgmt_sysfs_destroy_mig(struct platform_device *pdev)
+{
+	struct xocl_mig *mig;
+
+	mig = platform_get_drvdata(pdev);
+	sysfs_remove_group(&pdev->dev.kobj, &mig_attrgroup);
+}
+
+static int mgmt_sysfs_create_mig(struct platform_device *pdev)
+{
+	struct xocl_mig *mig;
+	int err;
+
+	mig = platform_get_drvdata(pdev);
+	err = sysfs_create_group(&pdev->dev.kobj, &mig_attrgroup);
+	if (err) {
+		xocl_err(&pdev->dev, "create pw group failed: 0x%x", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int mig_probe(struct platform_device *pdev)
+{
+	struct xocl_mig *mig;
+	struct resource *res;
+	int err;
+
+	mig = devm_kzalloc(&pdev->dev, sizeof(*mig), GFP_KERNEL);
+	if (!mig)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		xocl_err(&pdev->dev, "resource is NULL");
+		return -EINVAL;
+	}
+
+	xocl_info(&pdev->dev, "MIG name: %s, IO start: 0x%llx, end: 0x%llx",
+		XOCL_GET_SUBDEV_PRIV(&pdev->dev), res->start, res->end);
+
+	mig->base = ioremap_nocache(res->start, res->end - res->start + 1);
+	if (!mig->base) {
+		xocl_err(&pdev->dev, "Map iomem failed");
+		return -EIO;
+	}
+
+	platform_set_drvdata(pdev, mig);
+
+	err = mgmt_sysfs_create_mig(pdev);
+	if (err) {
+		platform_set_drvdata(pdev, NULL);
+		iounmap(mig->base);
+		return err;
+	}
+
+	return 0;
+}
+
+
+static int mig_remove(struct platform_device *pdev)
+{
+	struct xocl_mig	*mig;
+
+	mig = platform_get_drvdata(pdev);
+	if (!mig) {
+		xocl_err(&pdev->dev, "driver data is NULL");
+		return -EINVAL;
+	}
+
+	xocl_info(&pdev->dev, "MIG name: %s", XOCL_GET_SUBDEV_PRIV(&pdev->dev));
+
+	mgmt_sysfs_destroy_mig(pdev);
+
+	if (mig->base)
+		iounmap(mig->base);
+
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(&pdev->dev, mig);
+
+	return 0;
+}
+
+struct platform_device_id mig_id_table[] = {
+	{ XOCL_MIG, 0 },
+	{ },
+};
+
+static struct platform_driver	mig_driver = {
+	.probe		= mig_probe,
+	.remove		= mig_remove,
+	.driver		= {
+		.name = "xocl_mig",
+	},
+	.id_table = mig_id_table,
+};
+
+int __init xocl_init_mig(void)
+{
+	return platform_driver_register(&mig_driver);
+}
+
+void xocl_fini_mig(void)
+{
+	platform_driver_unregister(&mig_driver);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/sysmon.c b/drivers/gpu/drm/xocl/subdev/sysmon.c
new file mode 100644
index 000000000000..bb5c84485344
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/sysmon.c
@@ -0,0 +1,385 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2016-2018 Xilinx, Inc. All rights reserved.
+ *
+ * Authors:
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include "../xocl_drv.h"
+#include <drm/xmgmt_drm.h>
+
+#define TEMP		0x400		// TEMPOERATURE REGISTER ADDRESS
+#define VCCINT		0x404		// VCCINT REGISTER OFFSET
+#define VCCAUX		0x408		// VCCAUX REGISTER OFFSET
+#define VCCBRAM		0x418		// VCCBRAM REGISTER OFFSET
+#define	TEMP_MAX	0x480
+#define	VCCINT_MAX	0x484
+#define	VCCAUX_MAX	0x488
+#define	VCCBRAM_MAX	0x48c
+#define	TEMP_MIN	0x490
+#define	VCCINT_MIN	0x494
+#define	VCCAUX_MIN	0x498
+#define	VCCBRAM_MIN	0x49c
+
+#define	SYSMON_TO_MILLDEGREE(val)		\
+	(((int64_t)(val) * 501374 >> 16) - 273678)
+#define	SYSMON_TO_MILLVOLT(val)			\
+	((val) * 1000 * 3 >> 16)
+
+#define	READ_REG32(sysmon, off)		\
+	XOCL_READ_REG32(sysmon->base + off)
+#define	WRITE_REG32(sysmon, val, off)	\
+	XOCL_WRITE_REG32(val, sysmon->base + off)
+
+struct xocl_sysmon {
+	void __iomem		*base;
+	struct device		*hwmon_dev;
+};
+
+static int get_prop(struct platform_device *pdev, u32 prop, void *val)
+{
+	struct xocl_sysmon	*sysmon;
+	u32			tmp;
+
+	sysmon = platform_get_drvdata(pdev);
+	BUG_ON(!sysmon);
+
+	switch (prop) {
+	case XOCL_SYSMON_PROP_TEMP:
+		tmp = READ_REG32(sysmon, TEMP);
+		*(u32 *)val = SYSMON_TO_MILLDEGREE(tmp)/1000;
+		break;
+	case XOCL_SYSMON_PROP_TEMP_MAX:
+		tmp = READ_REG32(sysmon, TEMP_MAX);
+		*(u32 *)val = SYSMON_TO_MILLDEGREE(tmp);
+		break;
+	case XOCL_SYSMON_PROP_TEMP_MIN:
+		tmp = READ_REG32(sysmon, TEMP_MIN);
+		*(u32 *)val = SYSMON_TO_MILLDEGREE(tmp);
+		break;
+	case XOCL_SYSMON_PROP_VCC_INT:
+		tmp = READ_REG32(sysmon, VCCINT);
+		*(u32 *)val = SYSMON_TO_MILLVOLT(tmp);
+		break;
+	case XOCL_SYSMON_PROP_VCC_INT_MAX:
+		tmp = READ_REG32(sysmon, VCCINT_MAX);
+		*(u32 *)val = SYSMON_TO_MILLVOLT(tmp);
+		break;
+	case XOCL_SYSMON_PROP_VCC_INT_MIN:
+		tmp = READ_REG32(sysmon, VCCINT_MIN);
+		*(u32 *)val = SYSMON_TO_MILLVOLT(tmp);
+		break;
+	case XOCL_SYSMON_PROP_VCC_AUX:
+		tmp = READ_REG32(sysmon, VCCAUX);
+		*(u32 *)val = SYSMON_TO_MILLVOLT(tmp);
+		break;
+	case XOCL_SYSMON_PROP_VCC_AUX_MAX:
+		tmp = READ_REG32(sysmon, VCCAUX_MAX);
+		*(u32 *)val = SYSMON_TO_MILLVOLT(tmp);
+		break;
+	case XOCL_SYSMON_PROP_VCC_AUX_MIN:
+		tmp = READ_REG32(sysmon, VCCAUX_MIN);
+		*(u32 *)val = SYSMON_TO_MILLVOLT(tmp);
+		break;
+	case XOCL_SYSMON_PROP_VCC_BRAM:
+		tmp = READ_REG32(sysmon, VCCBRAM);
+		*(u32 *)val = SYSMON_TO_MILLVOLT(tmp);
+		break;
+	case XOCL_SYSMON_PROP_VCC_BRAM_MAX:
+		tmp = READ_REG32(sysmon, VCCBRAM_MAX);
+		*(u32 *)val = SYSMON_TO_MILLVOLT(tmp);
+		break;
+	case XOCL_SYSMON_PROP_VCC_BRAM_MIN:
+		tmp = READ_REG32(sysmon, VCCBRAM_MIN);
+		*(u32 *)val = SYSMON_TO_MILLVOLT(tmp);
+		break;
+	default:
+		xocl_err(&pdev->dev, "Invalid prop");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct xocl_sysmon_funcs sysmon_ops = {
+	.get_prop	= get_prop,
+};
+
+static ssize_t show_sysmon(struct platform_device *pdev, u32 prop, char *buf)
+{
+	u32 val;
+
+	(void) get_prop(pdev, prop, &val);
+	return sprintf(buf, "%u\n", val);
+}
+
+/* sysfs support */
+static ssize_t show_hwmon(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct platform_device *pdev = dev_get_drvdata(dev);
+
+	return show_sysmon(pdev, attr->index, buf);
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	return sprintf(buf, "%s\n", XCLMGMT_SYSMON_HWMON_NAME);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_TEMP);
+static SENSOR_DEVICE_ATTR(temp1_highest, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_TEMP_MAX);
+static SENSOR_DEVICE_ATTR(temp1_lowest, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_TEMP_MIN);
+
+static SENSOR_DEVICE_ATTR(in0_input, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_VCC_INT);
+static SENSOR_DEVICE_ATTR(in0_highest, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_VCC_INT_MAX);
+static SENSOR_DEVICE_ATTR(in0_lowest, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_VCC_INT_MIN);
+
+static SENSOR_DEVICE_ATTR(in1_input, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_VCC_AUX);
+static SENSOR_DEVICE_ATTR(in1_highest, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_VCC_AUX_MAX);
+static SENSOR_DEVICE_ATTR(in1_lowest, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_VCC_AUX_MIN);
+
+static SENSOR_DEVICE_ATTR(in2_input, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_VCC_BRAM);
+static SENSOR_DEVICE_ATTR(in2_highest, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_VCC_BRAM_MAX);
+static SENSOR_DEVICE_ATTR(in2_lowest, 0444, show_hwmon, NULL,
+	XOCL_SYSMON_PROP_VCC_BRAM_MIN);
+
+static struct attribute *hwmon_sysmon_attributes[] = {
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	&sensor_dev_attr_temp1_highest.dev_attr.attr,
+	&sensor_dev_attr_temp1_lowest.dev_attr.attr,
+	&sensor_dev_attr_in0_input.dev_attr.attr,
+	&sensor_dev_attr_in0_highest.dev_attr.attr,
+	&sensor_dev_attr_in0_lowest.dev_attr.attr,
+	&sensor_dev_attr_in1_input.dev_attr.attr,
+	&sensor_dev_attr_in1_highest.dev_attr.attr,
+	&sensor_dev_attr_in1_lowest.dev_attr.attr,
+	&sensor_dev_attr_in2_input.dev_attr.attr,
+	&sensor_dev_attr_in2_highest.dev_attr.attr,
+	&sensor_dev_attr_in2_lowest.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group hwmon_sysmon_attrgroup = {
+	.attrs = hwmon_sysmon_attributes,
+};
+
+static struct sensor_device_attribute sysmon_name_attr =
+	SENSOR_ATTR(name, 0444, show_name, NULL, 0);
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *da,
+			 char *buf)
+{
+	return show_sysmon(to_platform_device(dev), XOCL_SYSMON_PROP_TEMP, buf);
+}
+static DEVICE_ATTR_RO(temp);
+
+static ssize_t vcc_int_show(struct device *dev, struct device_attribute *da,
+			    char *buf)
+{
+	return show_sysmon(to_platform_device(dev), XOCL_SYSMON_PROP_VCC_INT, buf);
+}
+static DEVICE_ATTR_RO(vcc_int);
+
+static ssize_t vcc_aux_show(struct device *dev, struct device_attribute *da,
+			    char *buf)
+{
+	return show_sysmon(to_platform_device(dev), XOCL_SYSMON_PROP_VCC_AUX, buf);
+}
+static DEVICE_ATTR_RO(vcc_aux);
+
+static ssize_t vcc_bram_show(struct device *dev, struct device_attribute *da,
+			     char *buf)
+{
+	return show_sysmon(to_platform_device(dev), XOCL_SYSMON_PROP_VCC_BRAM, buf);
+}
+static DEVICE_ATTR_RO(vcc_bram);
+
+static struct attribute *sysmon_attributes[] = {
+	&dev_attr_temp.attr,
+	&dev_attr_vcc_int.attr,
+	&dev_attr_vcc_aux.attr,
+	&dev_attr_vcc_bram.attr,
+	NULL,
+};
+
+static const struct attribute_group sysmon_attrgroup = {
+	.attrs = sysmon_attributes,
+};
+
+static void mgmt_sysfs_destroy_sysmon(struct platform_device *pdev)
+{
+	struct xocl_sysmon *sysmon;
+
+	sysmon = platform_get_drvdata(pdev);
+
+	device_remove_file(sysmon->hwmon_dev, &sysmon_name_attr.dev_attr);
+	sysfs_remove_group(&sysmon->hwmon_dev->kobj, &hwmon_sysmon_attrgroup);
+	hwmon_device_unregister(sysmon->hwmon_dev);
+	sysmon->hwmon_dev = NULL;
+
+	sysfs_remove_group(&pdev->dev.kobj, &sysmon_attrgroup);
+}
+
+static int mgmt_sysfs_create_sysmon(struct platform_device *pdev)
+{
+	struct xocl_sysmon *sysmon;
+	struct xocl_dev_core *core;
+	int err;
+
+	sysmon = platform_get_drvdata(pdev);
+	core = XDEV(xocl_get_xdev(pdev));
+
+	sysmon->hwmon_dev = hwmon_device_register(&core->pdev->dev);
+	if (IS_ERR(sysmon->hwmon_dev)) {
+		err = PTR_ERR(sysmon->hwmon_dev);
+		xocl_err(&pdev->dev, "register sysmon hwmon failed: 0x%x", err);
+		goto hwmon_reg_failed;
+	}
+
+	dev_set_drvdata(sysmon->hwmon_dev, pdev);
+	err = device_create_file(sysmon->hwmon_dev,
+		&sysmon_name_attr.dev_attr);
+	if (err) {
+		xocl_err(&pdev->dev, "create attr name failed: 0x%x", err);
+		goto create_name_failed;
+	}
+
+	err = sysfs_create_group(&sysmon->hwmon_dev->kobj,
+		&hwmon_sysmon_attrgroup);
+	if (err) {
+		xocl_err(&pdev->dev, "create hwmon group failed: 0x%x", err);
+		goto create_hwmon_failed;
+	}
+
+	err = sysfs_create_group(&pdev->dev.kobj, &sysmon_attrgroup);
+	if (err) {
+		xocl_err(&pdev->dev, "create sysmon group failed: 0x%x", err);
+		goto create_sysmon_failed;
+	}
+
+	return 0;
+
+create_sysmon_failed:
+	sysfs_remove_group(&sysmon->hwmon_dev->kobj, &hwmon_sysmon_attrgroup);
+create_hwmon_failed:
+	device_remove_file(sysmon->hwmon_dev, &sysmon_name_attr.dev_attr);
+create_name_failed:
+	hwmon_device_unregister(sysmon->hwmon_dev);
+	sysmon->hwmon_dev = NULL;
+hwmon_reg_failed:
+	return err;
+}
+
+static int sysmon_probe(struct platform_device *pdev)
+{
+	struct xocl_sysmon *sysmon;
+	struct resource *res;
+	int err;
+
+	sysmon = devm_kzalloc(&pdev->dev, sizeof(*sysmon), GFP_KERNEL);
+	if (!sysmon)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		xocl_err(&pdev->dev, "resource is NULL");
+		return -EINVAL;
+	}
+	xocl_info(&pdev->dev, "IO start: 0x%llx, end: 0x%llx",
+		res->start, res->end);
+	sysmon->base = ioremap_nocache(res->start, res->end - res->start + 1);
+	if (!sysmon->base) {
+		err = -EIO;
+		xocl_err(&pdev->dev, "Map iomem failed");
+		goto failed;
+	}
+
+	platform_set_drvdata(pdev, sysmon);
+
+	err = mgmt_sysfs_create_sysmon(pdev);
+	if (err)
+		goto create_sysmon_failed;
+
+	xocl_subdev_register(pdev, XOCL_SUBDEV_SYSMON, &sysmon_ops);
+
+	return 0;
+
+create_sysmon_failed:
+	platform_set_drvdata(pdev, NULL);
+failed:
+	return err;
+}
+
+
+static int sysmon_remove(struct platform_device *pdev)
+{
+	struct xocl_sysmon	*sysmon;
+
+	sysmon = platform_get_drvdata(pdev);
+	if (!sysmon) {
+		xocl_err(&pdev->dev, "driver data is NULL");
+		return -EINVAL;
+	}
+
+	mgmt_sysfs_destroy_sysmon(pdev);
+
+	if (sysmon->base)
+		iounmap(sysmon->base);
+
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(&pdev->dev, sysmon);
+
+	return 0;
+}
+
+struct platform_device_id sysmon_id_table[] = {
+	{ XOCL_SYSMON, 0 },
+	{ },
+};
+
+static struct platform_driver	sysmon_driver = {
+	.probe		= sysmon_probe,
+	.remove		= sysmon_remove,
+	.driver		= {
+		.name = "xocl_sysmon",
+	},
+	.id_table = sysmon_id_table,
+};
+
+int __init xocl_init_sysmon(void)
+{
+	return platform_driver_register(&sysmon_driver);
+}
+
+void xocl_fini_sysmon(void)
+{
+	platform_driver_unregister(&sysmon_driver);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/xdma.c b/drivers/gpu/drm/xocl/subdev/xdma.c
new file mode 100644
index 000000000000..647a69f29a84
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/xdma.c
@@ -0,0 +1,510 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based accelerators.
+ *
+ * Copyright (C) 2016-2019 Xilinx, Inc. All rights reserved.
+ *
+ * Authors:
+ */
+
+/* XDMA version Memory Mapped DMA */
+
+#include <linux/version.h>
+#include <linux/eventfd.h>
+#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_mm.h>
+#include "../xocl_drv.h"
+#include "../xocl_drm.h"
+#include "../lib/libxdma_api.h"
+
+#define XOCL_FILE_PAGE_OFFSET   0x100000
+#ifndef VM_RESERVED
+#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
+#endif
+
+struct xdma_irq {
+	struct eventfd_ctx	*event_ctx;
+	bool			in_use;
+	bool			enabled;
+	irq_handler_t		handler;
+	void			*arg;
+};
+
+struct xocl_xdma {
+	void			*dma_handle;
+	u32			max_user_intr;
+	u32			start_user_intr;
+	struct xdma_irq		*user_msix_table;
+	struct mutex		user_msix_table_lock;
+
+	struct xocl_drm		*drm;
+	/* Number of bidirectional channels */
+	u32			channel;
+	/* Semaphore, one for each direction */
+	struct semaphore	channel_sem[2];
+	/*
+	 * Channel usage bitmasks, one for each direction
+	 * bit 1 indicates channel is free, bit 0 indicates channel is free
+	 */
+	unsigned long		channel_bitmap[2];
+	unsigned long long	*channel_usage[2];
+
+	struct mutex		stat_lock;
+};
+
+static ssize_t xdma_migrate_bo(struct platform_device *pdev,
+	struct sg_table *sgt, u32 dir, u64 paddr, u32 channel, u64 len)
+{
+	struct xocl_xdma *xdma;
+	struct page *pg;
+	struct scatterlist *sg = sgt->sgl;
+	int nents = sgt->orig_nents;
+	pid_t pid = current->pid;
+	int i = 0;
+	ssize_t ret;
+	unsigned long long pgaddr;
+
+	xdma = platform_get_drvdata(pdev);
+	xocl_dbg(&pdev->dev, "TID %d, Channel:%d, Offset: 0x%llx, Dir: %d",
+		pid, channel, paddr, dir);
+	ret = xdma_xfer_submit(xdma->dma_handle, channel, dir,
+		paddr, sgt, false, 10000);
+	if (ret >= 0) {
+		xdma->channel_usage[dir][channel] += ret;
+		return ret;
+	}
+
+	xocl_err(&pdev->dev, "DMA failed, Dumping SG Page Table");
+	for (i = 0; i < nents; i++, sg = sg_next(sg)) {
+		if (!sg)
+			break;
+		pg = sg_page(sg);
+		if (!pg)
+			continue;
+		pgaddr = page_to_phys(pg);
+		xocl_err(&pdev->dev, "%i, 0x%llx\n", i, pgaddr);
+	}
+	return ret;
+}
+
+static int acquire_channel(struct platform_device *pdev, u32 dir)
+{
+	struct xocl_xdma *xdma;
+	int channel = 0;
+	int result = 0;
+
+	xdma = platform_get_drvdata(pdev);
+	if (down_interruptible(&xdma->channel_sem[dir])) {
+		channel = -ERESTARTSYS;
+		goto out;
+	}
+
+	for (channel = 0; channel < xdma->channel; channel++) {
+		result = test_and_clear_bit(channel,
+			&xdma->channel_bitmap[dir]);
+		if (result)
+			break;
+	}
+	if (!result) {
+		// How is this possible?
+		up(&xdma->channel_sem[dir]);
+		channel = -EIO;
+	}
+
+out:
+	return channel;
+}
+
+static void release_channel(struct platform_device *pdev, u32 dir, u32 channel)
+{
+	struct xocl_xdma *xdma;
+
+
+	xdma = platform_get_drvdata(pdev);
+	set_bit(channel, &xdma->channel_bitmap[dir]);
+	up(&xdma->channel_sem[dir]);
+}
+
+static u32 get_channel_count(struct platform_device *pdev)
+{
+	struct xocl_xdma *xdma;
+
+	xdma = platform_get_drvdata(pdev);
+	BUG_ON(!xdma);
+
+	return xdma->channel;
+}
+
+static void *get_drm_handle(struct platform_device *pdev)
+{
+	struct xocl_xdma *xdma;
+
+	xdma = platform_get_drvdata(pdev);
+
+	return xdma->drm;
+}
+
+static u64 get_channel_stat(struct platform_device *pdev, u32 channel,
+	u32 write)
+{
+	struct xocl_xdma *xdma;
+
+	xdma = platform_get_drvdata(pdev);
+	BUG_ON(!xdma);
+
+	return xdma->channel_usage[write][channel];
+}
+
+static int user_intr_config(struct platform_device *pdev, u32 intr, bool en)
+{
+	struct xocl_xdma *xdma;
+	const unsigned int mask = 1 << intr;
+	int ret;
+
+	xdma = platform_get_drvdata(pdev);
+
+	if (intr >= xdma->max_user_intr) {
+		xocl_err(&pdev->dev, "Invalid intr %d, user start %d, max %d",
+			intr, xdma->start_user_intr, xdma->max_user_intr);
+		return -EINVAL;
+	}
+
+	mutex_lock(&xdma->user_msix_table_lock);
+	if (xdma->user_msix_table[intr].enabled == en) {
+		ret = 0;
+		goto end;
+	}
+
+	ret = en ? xdma_user_isr_enable(xdma->dma_handle, mask) :
+		xdma_user_isr_disable(xdma->dma_handle, mask);
+	if (!ret)
+		xdma->user_msix_table[intr].enabled = en;
+end:
+	mutex_unlock(&xdma->user_msix_table_lock);
+
+	return ret;
+}
+
+static irqreturn_t xdma_isr(int irq, void *arg)
+{
+	struct xdma_irq *irq_entry = arg;
+	int ret = IRQ_HANDLED;
+
+	if (irq_entry->handler)
+		ret = irq_entry->handler(irq, irq_entry->arg);
+
+	if (!IS_ERR_OR_NULL(irq_entry->event_ctx))
+		eventfd_signal(irq_entry->event_ctx, 1);
+
+	return ret;
+}
+
+static int user_intr_unreg(struct platform_device *pdev, u32 intr)
+{
+	struct xocl_xdma *xdma;
+	const unsigned int mask = 1 << intr;
+	int ret;
+
+	xdma = platform_get_drvdata(pdev);
+
+	if (intr >= xdma->max_user_intr)
+		return -EINVAL;
+
+	mutex_lock(&xdma->user_msix_table_lock);
+	if (!xdma->user_msix_table[intr].in_use) {
+		ret = -EINVAL;
+		goto failed;
+	}
+	xdma->user_msix_table[intr].handler = NULL;
+	xdma->user_msix_table[intr].arg = NULL;
+
+	ret = xdma_user_isr_register(xdma->dma_handle, mask, NULL, NULL);
+	if (ret) {
+		xocl_err(&pdev->dev, "xdma unregister isr failed");
+		goto failed;
+	}
+
+	xdma->user_msix_table[intr].in_use = false;
+
+failed:
+	mutex_unlock(&xdma->user_msix_table_lock);
+	return ret;
+}
+
+static int user_intr_register(struct platform_device *pdev, u32 intr,
+	irq_handler_t handler, void *arg, int event_fd)
+{
+	struct xocl_xdma *xdma;
+	struct eventfd_ctx *trigger = ERR_PTR(-EINVAL);
+	const unsigned int mask = 1 << intr;
+	int ret;
+
+	xdma = platform_get_drvdata(pdev);
+
+	if (intr >= xdma->max_user_intr ||
+			(event_fd >= 0 && intr < xdma->start_user_intr)) {
+		xocl_err(&pdev->dev, "Invalid intr %d, user start %d, max %d",
+			intr, xdma->start_user_intr, xdma->max_user_intr);
+		return -EINVAL;
+	}
+
+	if (event_fd >= 0) {
+		trigger = eventfd_ctx_fdget(event_fd);
+		if (IS_ERR(trigger)) {
+			xocl_err(&pdev->dev, "get event ctx failed");
+			return -EFAULT;
+		}
+	}
+
+	mutex_lock(&xdma->user_msix_table_lock);
+	if (xdma->user_msix_table[intr].in_use) {
+		xocl_err(&pdev->dev, "IRQ %d is in use", intr);
+		ret = -EPERM;
+		goto failed;
+	}
+	xdma->user_msix_table[intr].event_ctx = trigger;
+	xdma->user_msix_table[intr].handler = handler;
+	xdma->user_msix_table[intr].arg = arg;
+
+	ret = xdma_user_isr_register(xdma->dma_handle, mask, xdma_isr,
+			&xdma->user_msix_table[intr]);
+	if (ret) {
+		xocl_err(&pdev->dev, "IRQ register failed");
+		xdma->user_msix_table[intr].handler = NULL;
+		xdma->user_msix_table[intr].arg = NULL;
+		xdma->user_msix_table[intr].event_ctx = NULL;
+		goto failed;
+	}
+
+	xdma->user_msix_table[intr].in_use = true;
+
+	mutex_unlock(&xdma->user_msix_table_lock);
+
+
+	return 0;
+
+failed:
+	mutex_unlock(&xdma->user_msix_table_lock);
+	if (!IS_ERR(trigger))
+		eventfd_ctx_put(trigger);
+
+	return ret;
+}
+
+static struct xocl_dma_funcs xdma_ops = {
+	.migrate_bo = xdma_migrate_bo,
+	.ac_chan = acquire_channel,
+	.rel_chan = release_channel,
+	.get_chan_count = get_channel_count,
+	.get_chan_stat = get_channel_stat,
+	.user_intr_register = user_intr_register,
+	.user_intr_config = user_intr_config,
+	.user_intr_unreg = user_intr_unreg,
+	.get_drm_handle = get_drm_handle,
+};
+
+static ssize_t channel_stat_raw_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	u32 i;
+	ssize_t nbytes = 0;
+	struct platform_device *pdev = to_platform_device(dev);
+	u32 chs = get_channel_count(pdev);
+
+	for (i = 0; i < chs; i++) {
+		nbytes += sprintf(buf + nbytes, "%llu %llu\n",
+			get_channel_stat(pdev, i, 0),
+			get_channel_stat(pdev, i, 1));
+	}
+	return nbytes;
+}
+static DEVICE_ATTR_RO(channel_stat_raw);
+
+static struct attribute *xdma_attrs[] = {
+	&dev_attr_channel_stat_raw.attr,
+	NULL,
+};
+
+static struct attribute_group xdma_attr_group = {
+	.attrs = xdma_attrs,
+};
+
+static int set_max_chan(struct platform_device *pdev,
+		struct xocl_xdma *xdma)
+{
+	xdma->channel_usage[0] = devm_kzalloc(&pdev->dev, sizeof(u64) *
+		xdma->channel, GFP_KERNEL);
+	xdma->channel_usage[1] = devm_kzalloc(&pdev->dev, sizeof(u64) *
+		xdma->channel, GFP_KERNEL);
+	if (!xdma->channel_usage[0] || !xdma->channel_usage[1]) {
+		xocl_err(&pdev->dev, "failed to alloc channel usage");
+		return -ENOMEM;
+	}
+
+	sema_init(&xdma->channel_sem[0], xdma->channel);
+	sema_init(&xdma->channel_sem[1], xdma->channel);
+
+	/* Initialize bit mask to represent individual channels */
+	xdma->channel_bitmap[0] = BIT(xdma->channel);
+	xdma->channel_bitmap[0]--;
+	xdma->channel_bitmap[1] = xdma->channel_bitmap[0];
+
+	return 0;
+}
+
+static int xdma_probe(struct platform_device *pdev)
+{
+	struct xocl_xdma	*xdma = NULL;
+	int	ret = 0;
+	xdev_handle_t		xdev;
+
+	xdev = xocl_get_xdev(pdev);
+	BUG_ON(!xdev);
+
+	xdma = devm_kzalloc(&pdev->dev, sizeof(*xdma), GFP_KERNEL);
+	if (!xdma) {
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	xdma->dma_handle = xdma_device_open(XOCL_MODULE_NAME, XDEV(xdev)->pdev,
+			&xdma->max_user_intr,
+			&xdma->channel, &xdma->channel);
+	if (xdma->dma_handle == NULL) {
+		xocl_err(&pdev->dev, "XDMA Device Open failed");
+		ret = -EIO;
+		goto failed;
+	}
+
+	xdma->user_msix_table = devm_kzalloc(&pdev->dev,
+			xdma->max_user_intr *
+			sizeof(struct xdma_irq), GFP_KERNEL);
+	if (!xdma->user_msix_table) {
+		xocl_err(&pdev->dev, "alloc user_msix_table failed");
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	ret = set_max_chan(pdev, xdma);
+	if (ret) {
+		xocl_err(&pdev->dev, "Set max channel failed");
+		goto failed;
+	}
+
+	xdma->drm = xocl_drm_init(xdev);
+	if (!xdma->drm) {
+		ret = -EFAULT;
+		xocl_err(&pdev->dev, "failed to init drm mm");
+		goto failed;
+	}
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &xdma_attr_group);
+	if (ret) {
+		xocl_err(&pdev->dev, "create attrs failed: %d", ret);
+		goto failed;
+	}
+
+	mutex_init(&xdma->stat_lock);
+	mutex_init(&xdma->user_msix_table_lock);
+
+	xocl_subdev_register(pdev, XOCL_SUBDEV_DMA, &xdma_ops);
+	platform_set_drvdata(pdev, xdma);
+
+	return 0;
+
+failed:
+	if (xdma) {
+		if (xdma->drm)
+			xocl_drm_fini(xdma->drm);
+		if (xdma->dma_handle)
+			xdma_device_close(XDEV(xdev)->pdev, xdma->dma_handle);
+		if (xdma->channel_usage[0])
+			devm_kfree(&pdev->dev, xdma->channel_usage[0]);
+		if (xdma->channel_usage[1])
+			devm_kfree(&pdev->dev, xdma->channel_usage[1]);
+		if (xdma->user_msix_table)
+			devm_kfree(&pdev->dev, xdma->user_msix_table);
+
+		devm_kfree(&pdev->dev, xdma);
+	}
+
+	platform_set_drvdata(pdev, NULL);
+
+	return ret;
+}
+
+static int xdma_remove(struct platform_device *pdev)
+{
+	struct xocl_xdma *xdma = platform_get_drvdata(pdev);
+	xdev_handle_t xdev;
+	struct xdma_irq *irq_entry;
+	int i;
+
+	if (!xdma) {
+		xocl_err(&pdev->dev, "driver data is NULL");
+		return -EINVAL;
+	}
+
+	xdev = xocl_get_xdev(pdev);
+	BUG_ON(!xdev);
+
+	sysfs_remove_group(&pdev->dev.kobj, &xdma_attr_group);
+
+	if (xdma->drm)
+		xocl_drm_fini(xdma->drm);
+	if (xdma->dma_handle)
+		xdma_device_close(XDEV(xdev)->pdev, xdma->dma_handle);
+
+	for (i = 0; i < xdma->max_user_intr; i++) {
+		irq_entry = &xdma->user_msix_table[i];
+		if (irq_entry->in_use) {
+			if (irq_entry->enabled) {
+				xocl_err(&pdev->dev,
+					"ERROR: Interrupt %d is still on", i);
+			}
+			if (!IS_ERR_OR_NULL(irq_entry->event_ctx))
+				eventfd_ctx_put(irq_entry->event_ctx);
+		}
+	}
+
+	if (xdma->channel_usage[0])
+		devm_kfree(&pdev->dev, xdma->channel_usage[0]);
+	if (xdma->channel_usage[1])
+		devm_kfree(&pdev->dev, xdma->channel_usage[1]);
+
+	mutex_destroy(&xdma->stat_lock);
+	mutex_destroy(&xdma->user_msix_table_lock);
+
+	devm_kfree(&pdev->dev, xdma->user_msix_table);
+	platform_set_drvdata(pdev, NULL);
+
+	devm_kfree(&pdev->dev, xdma);
+
+	return 0;
+}
+
+static struct platform_device_id xdma_id_table[] = {
+	{ XOCL_XDMA, 0 },
+	{ },
+};
+
+static struct platform_driver	xdma_driver = {
+	.probe		= xdma_probe,
+	.remove		= xdma_remove,
+	.driver		= {
+		.name = "xocl_xdma",
+	},
+	.id_table	= xdma_id_table,
+};
+
+int __init xocl_init_xdma(void)
+{
+	return platform_driver_register(&xdma_driver);
+}
+
+void xocl_fini_xdma(void)
+{
+	return platform_driver_unregister(&xdma_driver);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/xmc.c b/drivers/gpu/drm/xocl/subdev/xmc.c
new file mode 100644
index 000000000000..d9d620ac09b9
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/xmc.c
@@ -0,0 +1,1480 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2016-2018 Xilinx, Inc. All rights reserved.
+ *
+ * Authors: chienwei@xilinx.com
+ *
+ */
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+#include "../ert.h"
+#include "../xocl_drv.h"
+#include <drm/xmgmt_drm.h>
+
+#define MAX_XMC_RETRY       150	//Retry is set to 15s for XMC
+#define MAX_ERT_RETRY       10	//Retry is set to 1s for ERT
+#define RETRY_INTERVAL  100       //100ms
+
+#define	MAX_IMAGE_LEN	0x20000
+
+#define XMC_MAGIC_REG               0x0
+#define XMC_VERSION_REG             0x4
+#define XMC_STATUS_REG              0x8
+#define XMC_ERROR_REG               0xC
+#define XMC_FEATURE_REG             0x10
+#define XMC_SENSOR_REG              0x14
+#define XMC_CONTROL_REG             0x18
+#define XMC_STOP_CONFIRM_REG        0x1C
+#define XMC_12V_PEX_REG             0x20
+#define XMC_3V3_PEX_REG             0x2C
+#define XMC_3V3_AUX_REG             0x38
+#define XMC_12V_AUX_REG             0x44
+#define XMC_DDR4_VPP_BTM_REG        0x50
+#define XMC_SYS_5V5_REG             0x5C
+#define XMC_VCC1V2_TOP_REG          0x68
+#define XMC_VCC1V8_REG              0x74
+#define XMC_VCC0V85_REG             0x80
+#define XMC_DDR4_VPP_TOP_REG        0x8C
+#define XMC_MGT0V9AVCC_REG          0x98
+#define XMC_12V_SW_REG              0xA4
+#define XMC_MGTAVTT_REG             0xB0
+#define XMC_VCC1V2_BTM_REG          0xBC
+#define XMC_12V_PEX_I_IN_REG        0xC8
+#define XMC_12V_AUX_I_IN_REG        0xD4
+#define XMC_VCCINT_V_REG            0xE0
+#define XMC_VCCINT_I_REG            0xEC
+#define XMC_FPGA_TEMP               0xF8
+#define XMC_FAN_TEMP_REG            0x104
+#define XMC_DIMM_TEMP0_REG          0x110
+#define XMC_DIMM_TEMP1_REG          0x11C
+#define XMC_DIMM_TEMP2_REG          0x128
+#define XMC_DIMM_TEMP3_REG          0x134
+#define XMC_FAN_SPEED_REG           0x164
+#define XMC_SE98_TEMP0_REG          0x140
+#define XMC_SE98_TEMP1_REG          0x14C
+#define XMC_SE98_TEMP2_REG          0x158
+#define XMC_CAGE_TEMP0_REG          0x170
+#define XMC_CAGE_TEMP1_REG          0x17C
+#define XMC_CAGE_TEMP2_REG          0x188
+#define XMC_CAGE_TEMP3_REG          0x194
+#define XMC_SNSR_CHKSUM_REG         0x1A4
+#define XMC_SNSR_FLAGS_REG          0x1A8
+#define XMC_HOST_MSG_OFFSET_REG     0x300
+#define XMC_HOST_MSG_ERROR_REG      0x304
+#define XMC_HOST_MSG_HEADER_REG     0x308
+
+
+#define	VALID_ID		0x74736574
+
+#define	GPIO_RESET		0x0
+#define	GPIO_ENABLED		0x1
+
+#define	SELF_JUMP(ins)		(((ins) & 0xfc00ffff) == 0xb8000000)
+#define	XMC_PRIVILEGED(xmc)	((xmc)->base_addrs[0] != NULL)
+
+enum ctl_mask {
+	CTL_MASK_CLEAR_POW	= 0x1,
+	CTL_MASK_CLEAR_ERR	= 0x2,
+	CTL_MASK_PAUSE		= 0x4,
+	CTL_MASK_STOP		= 0x8,
+};
+
+enum status_mask {
+	STATUS_MASK_INIT_DONE		= 0x1,
+	STATUS_MASK_STOPPED		= 0x2,
+	STATUS_MASK_PAUSE		= 0x4,
+};
+
+enum cap_mask {
+	CAP_MASK_PM			= 0x1,
+};
+
+enum {
+	XMC_STATE_UNKNOWN,
+	XMC_STATE_ENABLED,
+	XMC_STATE_RESET,
+	XMC_STATE_STOPPED,
+	XMC_STATE_ERROR
+};
+
+enum {
+	IO_REG,
+	IO_GPIO,
+	IO_IMAGE_MGMT,
+	IO_IMAGE_SCHED,
+	IO_CQ,
+	NUM_IOADDR
+};
+
+enum {
+	VOLTAGE_MAX,
+	VOLTAGE_AVG,
+	VOLTAGE_INS,
+};
+
+#define	READ_REG32(xmc, off)		\
+	XOCL_READ_REG32(xmc->base_addrs[IO_REG] + off)
+#define	WRITE_REG32(xmc, val, off)	\
+	XOCL_WRITE_REG32(val, xmc->base_addrs[IO_REG] + off)
+
+#define	READ_GPIO(xmc, off)		\
+	XOCL_READ_REG32(xmc->base_addrs[IO_GPIO] + off)
+#define	WRITE_GPIO(xmc, val, off)	\
+	XOCL_WRITE_REG32(val, xmc->base_addrs[IO_GPIO] + off)
+
+#define	READ_IMAGE_MGMT(xmc, off)		\
+	XOCL_READ_REG32(xmc->base_addrs[IO_IMAGE_MGMT] + off)
+
+#define	READ_IMAGE_SCHED(xmc, off)		\
+	XOCL_READ_REG32(xmc->base_addrs[IO_IMAGE_SCHED] + off)
+
+#define	COPY_MGMT(xmc, buf, len)		\
+	xocl_memcpy_toio(xmc->base_addrs[IO_IMAGE_MGMT], buf, len)
+#define	COPY_SCHE(xmc, buf, len)		\
+	xocl_memcpy_toio(xmc->base_addrs[IO_IMAGE_SCHED], buf, len)
+
+struct xocl_xmc {
+	struct platform_device	*pdev;
+	void __iomem		*base_addrs[NUM_IOADDR];
+
+	struct device		*hwmon_dev;
+	bool			enabled;
+	u32			state;
+	u32			cap;
+	struct mutex		xmc_lock;
+
+	char			*sche_binary;
+	u32			sche_binary_length;
+	char			*mgmt_binary;
+	u32			mgmt_binary_length;
+};
+
+
+static int load_xmc(struct xocl_xmc *xmc);
+static int stop_xmc(struct platform_device *pdev);
+
+static void xmc_read_from_peer(struct platform_device *pdev, enum data_kind kind, void *resp, size_t resplen)
+{
+	struct mailbox_subdev_peer subdev_peer = {0};
+	size_t data_len = sizeof(struct mailbox_subdev_peer);
+	struct mailbox_req *mb_req = NULL;
+	size_t reqlen = sizeof(struct mailbox_req) + data_len;
+
+	mb_req = vmalloc(reqlen);
+	if (!mb_req)
+		return;
+
+	mb_req->req = MAILBOX_REQ_PEER_DATA;
+
+	subdev_peer.kind = kind;
+	memcpy(mb_req->data, &subdev_peer, data_len);
+
+	(void) xocl_peer_request(XOCL_PL_DEV_TO_XDEV(pdev),
+		mb_req, reqlen, resp, &resplen, NULL, NULL);
+	vfree(mb_req);
+}
+
+/* sysfs support */
+static void safe_read32(struct xocl_xmc *xmc, u32 reg, u32 *val)
+{
+	mutex_lock(&xmc->xmc_lock);
+	if (xmc->enabled && xmc->state == XMC_STATE_ENABLED)
+		*val = READ_REG32(xmc, reg);
+	else
+		*val = 0;
+
+	mutex_unlock(&xmc->xmc_lock);
+}
+
+static void safe_write32(struct xocl_xmc *xmc, u32 reg, u32 val)
+{
+	mutex_lock(&xmc->xmc_lock);
+	if (xmc->enabled && xmc->state == XMC_STATE_ENABLED)
+		WRITE_REG32(xmc, val, reg);
+
+	mutex_unlock(&xmc->xmc_lock);
+}
+
+static void safe_read_from_peer(struct xocl_xmc *xmc, struct platform_device *pdev, enum data_kind kind, u32 *val)
+{
+	mutex_lock(&xmc->xmc_lock);
+	if (xmc->enabled)
+		xmc_read_from_peer(pdev, kind, val, sizeof(u32));
+	else
+		*val = 0;
+
+	mutex_unlock(&xmc->xmc_lock);
+}
+
+static int xmc_get_data(struct platform_device *pdev, enum data_kind kind)
+{
+	struct xocl_xmc *xmc = platform_get_drvdata(pdev);
+	int val;
+
+	if (XMC_PRIVILEGED(xmc)) {
+		switch (kind) {
+		case VOL_12V_PEX:
+			safe_read32(xmc, XMC_12V_PEX_REG + sizeof(u32)*VOLTAGE_INS, &val);
+			break;
+		default:
+			break;
+		}
+	}
+	return val;
+}
+
+static ssize_t xmc_12v_pex_vol_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 pes_val;
+
+	if (XMC_PRIVILEGED(xmc))
+		safe_read32(xmc, XMC_12V_PEX_REG+sizeof(u32)*VOLTAGE_INS, &pes_val);
+	else
+		safe_read_from_peer(xmc, to_platform_device(dev), VOL_12V_PEX, &pes_val);
+
+	return sprintf(buf, "%d\n", pes_val);
+}
+static DEVICE_ATTR_RO(xmc_12v_pex_vol);
+
+static ssize_t xmc_12v_aux_vol_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_12V_AUX_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_12v_aux_vol);
+
+static ssize_t xmc_12v_pex_curr_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 pes_val;
+
+	safe_read32(xmc, XMC_12V_PEX_I_IN_REG+sizeof(u32)*VOLTAGE_INS, &pes_val);
+
+	return sprintf(buf, "%d\n", pes_val);
+}
+static DEVICE_ATTR_RO(xmc_12v_pex_curr);
+
+static ssize_t xmc_12v_aux_curr_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_12V_AUX_I_IN_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_12v_aux_curr);
+
+static ssize_t xmc_3v3_pex_vol_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_3V3_PEX_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_3v3_pex_vol);
+
+static ssize_t xmc_3v3_aux_vol_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_3V3_AUX_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_3v3_aux_vol);
+
+static ssize_t xmc_ddr_vpp_btm_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_DDR4_VPP_BTM_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_ddr_vpp_btm);
+
+static ssize_t xmc_sys_5v5_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_SYS_5V5_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_sys_5v5);
+
+static ssize_t xmc_1v2_top_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_VCC1V2_TOP_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_1v2_top);
+
+static ssize_t xmc_1v8_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_VCC1V8_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_1v8);
+
+static ssize_t xmc_0v85_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_VCC0V85_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_0v85);
+
+static ssize_t xmc_ddr_vpp_top_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_DDR4_VPP_TOP_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_ddr_vpp_top);
+
+
+static ssize_t xmc_mgt0v9avcc_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_MGT0V9AVCC_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_mgt0v9avcc);
+
+static ssize_t xmc_12v_sw_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_12V_SW_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_12v_sw);
+
+
+static ssize_t xmc_mgtavtt_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_MGTAVTT_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_mgtavtt);
+
+static ssize_t xmc_vcc1v2_btm_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_VCC1V2_BTM_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_vcc1v2_btm);
+
+static ssize_t xmc_vccint_vol_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_VCCINT_V_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_vccint_vol);
+
+static ssize_t xmc_vccint_curr_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_VCCINT_I_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_vccint_curr);
+
+static ssize_t xmc_se98_temp0_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_SE98_TEMP0_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_se98_temp0);
+
+static ssize_t xmc_se98_temp1_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_SE98_TEMP1_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_se98_temp1);
+
+static ssize_t xmc_se98_temp2_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_SE98_TEMP2_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_se98_temp2);
+
+static ssize_t xmc_fpga_temp_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_FPGA_TEMP, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_fpga_temp);
+
+static ssize_t xmc_fan_temp_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_FAN_TEMP_REG, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_fan_temp);
+
+static ssize_t xmc_fan_rpm_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_FAN_SPEED_REG, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_fan_rpm);
+
+
+static ssize_t xmc_dimm_temp0_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_DIMM_TEMP0_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_dimm_temp0);
+
+static ssize_t xmc_dimm_temp1_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_DIMM_TEMP1_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_dimm_temp1);
+
+static ssize_t xmc_dimm_temp2_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_DIMM_TEMP2_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_dimm_temp2);
+
+static ssize_t xmc_dimm_temp3_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_DIMM_TEMP3_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_dimm_temp3);
+
+
+static ssize_t xmc_cage_temp0_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_CAGE_TEMP0_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_cage_temp0);
+
+static ssize_t xmc_cage_temp1_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_CAGE_TEMP1_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_cage_temp1);
+
+static ssize_t xmc_cage_temp2_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_CAGE_TEMP2_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_cage_temp2);
+
+static ssize_t xmc_cage_temp3_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_CAGE_TEMP3_REG+sizeof(u32)*VOLTAGE_INS, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(xmc_cage_temp3);
+
+
+static ssize_t version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_xmc *xmc = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(xmc, XMC_VERSION_REG, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(version);
+
+static ssize_t sensor_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_xmc *xmc = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(xmc, XMC_SENSOR_REG, &val);
+
+	return sprintf(buf, "0x%04x\n", val);
+}
+static DEVICE_ATTR_RO(sensor);
+
+
+static ssize_t id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_xmc *xmc = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(xmc, XMC_MAGIC_REG, &val);
+
+	return sprintf(buf, "%x\n", val);
+}
+static DEVICE_ATTR_RO(id);
+
+static ssize_t status_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_xmc *xmc = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(xmc, XMC_STATUS_REG, &val);
+
+	return sprintf(buf, "%x\n", val);
+}
+static DEVICE_ATTR_RO(status);
+
+static ssize_t error_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_xmc *xmc = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(xmc, XMC_ERROR_REG, &val);
+
+	return sprintf(buf, "%x\n", val);
+}
+static DEVICE_ATTR_RO(error);
+
+static ssize_t capability_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_xmc *xmc = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(xmc, XMC_FEATURE_REG, &val);
+
+	return sprintf(buf, "%x\n", val);
+}
+static DEVICE_ATTR_RO(capability);
+
+static ssize_t power_checksum_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_xmc *xmc = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(xmc, XMC_SNSR_CHKSUM_REG, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(power_checksum);
+
+static ssize_t pause_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct xocl_xmc *xmc = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	safe_read32(xmc, XMC_CONTROL_REG, &val);
+
+	return sprintf(buf, "%d\n", !!(val & CTL_MASK_PAUSE));
+}
+
+static ssize_t pause_store(struct device *dev,
+	struct device_attribute *da, const char *buf, size_t count)
+{
+	struct xocl_xmc *xmc = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	if (kstrtou32(buf, 10, &val) == -EINVAL || val > 1)
+		return -EINVAL;
+
+	val = val ? CTL_MASK_PAUSE : 0;
+	safe_write32(xmc, XMC_CONTROL_REG, val);
+
+	return count;
+}
+static DEVICE_ATTR_RW(pause);
+
+static ssize_t reset_store(struct device *dev,
+	struct device_attribute *da, const char *buf, size_t count)
+{
+	struct xocl_xmc *xmc = platform_get_drvdata(to_platform_device(dev));
+	u32 val;
+
+	if (kstrtou32(buf, 10, &val) == -EINVAL || val > 1)
+		return -EINVAL;
+
+	if (val)
+		load_xmc(xmc);
+
+	return count;
+}
+static DEVICE_ATTR_WO(reset);
+
+static ssize_t power_flag_show(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_SNSR_FLAGS_REG, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(power_flag);
+
+static ssize_t host_msg_offset_show(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_HOST_MSG_OFFSET_REG, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(host_msg_offset);
+
+static ssize_t host_msg_error_show(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_HOST_MSG_ERROR_REG, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(host_msg_error);
+
+static ssize_t host_msg_header_show(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_HOST_MSG_HEADER_REG, &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+static DEVICE_ATTR_RO(host_msg_header);
+
+
+
+static int get_temp_by_m_tag(struct xocl_xmc *xmc, char *m_tag)
+{
+
+	/**
+	 *   m_tag get from xclbin must follow this format
+	 *   DDR[0] or bank1
+	 *   we check the index in m_tag to decide which temperature
+	 *   to get from XMC IP base address
+	 */
+	char *start = NULL, *left_parentness = NULL, *right_parentness = NULL;
+	long idx;
+	int ret = 0, digit_len = 0;
+	char temp[4];
+
+	if (!xmc)
+		return -ENODEV;
+
+
+	if (!strncmp(m_tag, "bank", 4)) {
+		start = m_tag;
+		// bank0, no left parentness
+		left_parentness = m_tag+3;
+		right_parentness = m_tag+strlen(m_tag)+1;
+		digit_len = right_parentness-(2+left_parentness);
+	} else if (!strncmp(m_tag, "DDR", 3)) {
+
+		start = m_tag;
+		left_parentness = strstr(m_tag, "[");
+		right_parentness = strstr(m_tag, "]");
+		digit_len = right_parentness-(1+left_parentness);
+	}
+
+	if (!left_parentness || !right_parentness)
+		return ret;
+
+	if (!strncmp(m_tag, "DDR", left_parentness-start) || !strncmp(m_tag, "bank", left_parentness-start)) {
+
+		strncpy(temp, left_parentness+1, digit_len);
+		//assumption, temperature won't higher than 3 digits, or the temp[digit_len] should be a null character
+		temp[digit_len] = '\0';
+		//convert to signed long, decimal base
+		if (kstrtol(temp, 10, &idx) == 0 && idx < 4 && idx >= 0)
+			safe_read32(xmc, XMC_DIMM_TEMP0_REG + (3*sizeof(int32_t)) * idx +
+				    sizeof(u32)*VOLTAGE_INS, &ret);
+		else
+			ret = 0;
+	}
+
+	return ret;
+
+}
+
+static struct attribute *xmc_attrs[] = {
+	&dev_attr_version.attr,
+	&dev_attr_id.attr,
+	&dev_attr_status.attr,
+	&dev_attr_sensor.attr,
+	&dev_attr_error.attr,
+	&dev_attr_capability.attr,
+	&dev_attr_power_checksum.attr,
+	&dev_attr_xmc_12v_pex_vol.attr,
+	&dev_attr_xmc_12v_aux_vol.attr,
+	&dev_attr_xmc_12v_pex_curr.attr,
+	&dev_attr_xmc_12v_aux_curr.attr,
+	&dev_attr_xmc_3v3_pex_vol.attr,
+	&dev_attr_xmc_3v3_aux_vol.attr,
+	&dev_attr_xmc_ddr_vpp_btm.attr,
+	&dev_attr_xmc_sys_5v5.attr,
+	&dev_attr_xmc_1v2_top.attr,
+	&dev_attr_xmc_1v8.attr,
+	&dev_attr_xmc_0v85.attr,
+	&dev_attr_xmc_ddr_vpp_top.attr,
+	&dev_attr_xmc_mgt0v9avcc.attr,
+	&dev_attr_xmc_12v_sw.attr,
+	&dev_attr_xmc_mgtavtt.attr,
+	&dev_attr_xmc_vcc1v2_btm.attr,
+	&dev_attr_xmc_fpga_temp.attr,
+	&dev_attr_xmc_fan_temp.attr,
+	&dev_attr_xmc_fan_rpm.attr,
+	&dev_attr_xmc_dimm_temp0.attr,
+	&dev_attr_xmc_dimm_temp1.attr,
+	&dev_attr_xmc_dimm_temp2.attr,
+	&dev_attr_xmc_dimm_temp3.attr,
+	&dev_attr_xmc_vccint_vol.attr,
+	&dev_attr_xmc_vccint_curr.attr,
+	&dev_attr_xmc_se98_temp0.attr,
+	&dev_attr_xmc_se98_temp1.attr,
+	&dev_attr_xmc_se98_temp2.attr,
+	&dev_attr_xmc_cage_temp0.attr,
+	&dev_attr_xmc_cage_temp1.attr,
+	&dev_attr_xmc_cage_temp2.attr,
+	&dev_attr_xmc_cage_temp3.attr,
+	&dev_attr_pause.attr,
+	&dev_attr_reset.attr,
+	&dev_attr_power_flag.attr,
+	&dev_attr_host_msg_offset.attr,
+	&dev_attr_host_msg_error.attr,
+	&dev_attr_host_msg_header.attr,
+	NULL,
+};
+
+
+static ssize_t read_temp_by_mem_topology(struct file *filp, struct kobject *kobj,
+	struct bin_attribute *attr, char *buffer, loff_t offset, size_t count)
+{
+	u32 nread = 0;
+	size_t size = 0;
+	u32 i;
+	struct mem_topology *memtopo = NULL;
+	struct xocl_xmc *xmc;
+	uint32_t temp[MAX_M_COUNT] = {0};
+	struct xclmgmt_dev *lro;
+
+	//xocl_icap_lock_bitstream
+	lro = (struct xclmgmt_dev *)dev_get_drvdata(container_of(kobj, struct device, kobj)->parent);
+	xmc = (struct xocl_xmc *)dev_get_drvdata(container_of(kobj, struct device, kobj));
+
+	memtopo = (struct mem_topology *)xocl_icap_get_data(lro, MEMTOPO_AXLF);
+
+	if (!memtopo)
+		return 0;
+
+	size = sizeof(u32)*(memtopo->m_count);
+
+	if (offset >= size)
+		return 0;
+
+	for (i = 0; i < memtopo->m_count; ++i)
+		*(temp+i) = get_temp_by_m_tag(xmc, memtopo->m_mem_data[i].m_tag);
+
+	if (count < size - offset)
+		nread = count;
+	else
+		nread = size - offset;
+
+	memcpy(buffer, temp, nread);
+	//xocl_icap_unlock_bitstream
+	return nread;
+}
+
+static struct bin_attribute bin_dimm_temp_by_mem_topology_attr = {
+	.attr = {
+		.name = "temp_by_mem_topology",
+		.mode = 0444
+	},
+	.read = read_temp_by_mem_topology,
+	.write = NULL,
+	.size = 0
+};
+
+static struct bin_attribute *xmc_bin_attrs[] = {
+	&bin_dimm_temp_by_mem_topology_attr,
+	NULL,
+};
+
+static struct attribute_group xmc_attr_group = {
+	.attrs = xmc_attrs,
+	.bin_attrs = xmc_bin_attrs,
+};
+static ssize_t show_mb_pw(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct xocl_xmc *xmc = dev_get_drvdata(dev);
+	u32 val;
+
+	safe_read32(xmc, XMC_12V_PEX_REG + attr->index * sizeof(u32), &val);
+
+	return sprintf(buf, "%d\n", val);
+}
+
+static SENSOR_DEVICE_ATTR(curr1_highest, 0444, show_mb_pw, NULL, 0);
+static SENSOR_DEVICE_ATTR(curr1_average, 0444, show_mb_pw, NULL, 1);
+static SENSOR_DEVICE_ATTR(curr1_input, 0444, show_mb_pw, NULL, 2);
+static SENSOR_DEVICE_ATTR(curr2_highest, 0444, show_mb_pw, NULL, 3);
+static SENSOR_DEVICE_ATTR(curr2_average, 0444, show_mb_pw, NULL, 4);
+static SENSOR_DEVICE_ATTR(curr2_input, 0444, show_mb_pw, NULL, 5);
+static SENSOR_DEVICE_ATTR(curr3_highest, 0444, show_mb_pw, NULL, 6);
+static SENSOR_DEVICE_ATTR(curr3_average, 0444, show_mb_pw, NULL, 7);
+static SENSOR_DEVICE_ATTR(curr3_input, 0444, show_mb_pw, NULL, 8);
+static SENSOR_DEVICE_ATTR(curr4_highest, 0444, show_mb_pw, NULL, 9);
+static SENSOR_DEVICE_ATTR(curr4_average, 0444, show_mb_pw, NULL, 10);
+static SENSOR_DEVICE_ATTR(curr4_input, 0444, show_mb_pw, NULL, 11);
+static SENSOR_DEVICE_ATTR(curr5_highest, 0444, show_mb_pw, NULL, 12);
+static SENSOR_DEVICE_ATTR(curr5_average, 0444, show_mb_pw, NULL, 13);
+static SENSOR_DEVICE_ATTR(curr5_input, 0444, show_mb_pw, NULL, 14);
+static SENSOR_DEVICE_ATTR(curr6_highest, 0444, show_mb_pw, NULL, 15);
+static SENSOR_DEVICE_ATTR(curr6_average, 0444, show_mb_pw, NULL, 16);
+static SENSOR_DEVICE_ATTR(curr6_input, 0444, show_mb_pw, NULL, 17);
+
+static struct attribute *hwmon_xmc_attributes[] = {
+	&sensor_dev_attr_curr1_highest.dev_attr.attr,
+	&sensor_dev_attr_curr1_average.dev_attr.attr,
+	&sensor_dev_attr_curr1_input.dev_attr.attr,
+	&sensor_dev_attr_curr2_highest.dev_attr.attr,
+	&sensor_dev_attr_curr2_average.dev_attr.attr,
+	&sensor_dev_attr_curr2_input.dev_attr.attr,
+	&sensor_dev_attr_curr3_highest.dev_attr.attr,
+	&sensor_dev_attr_curr3_average.dev_attr.attr,
+	&sensor_dev_attr_curr3_input.dev_attr.attr,
+	&sensor_dev_attr_curr4_highest.dev_attr.attr,
+	&sensor_dev_attr_curr4_average.dev_attr.attr,
+	&sensor_dev_attr_curr4_input.dev_attr.attr,
+	&sensor_dev_attr_curr5_highest.dev_attr.attr,
+	&sensor_dev_attr_curr5_average.dev_attr.attr,
+	&sensor_dev_attr_curr5_input.dev_attr.attr,
+	&sensor_dev_attr_curr6_highest.dev_attr.attr,
+	&sensor_dev_attr_curr6_average.dev_attr.attr,
+	&sensor_dev_attr_curr6_input.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group hwmon_xmc_attrgroup = {
+	.attrs = hwmon_xmc_attributes,
+};
+
+static ssize_t show_name(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	return sprintf(buf, "%s\n", XCLMGMT_MB_HWMON_NAME);
+}
+
+static struct sensor_device_attribute name_attr =
+	SENSOR_ATTR(name, 0444, show_name, NULL, 0);
+
+static void mgmt_sysfs_destroy_xmc(struct platform_device *pdev)
+{
+	struct xocl_xmc *xmc;
+
+	xmc = platform_get_drvdata(pdev);
+
+	if (!xmc->enabled)
+		return;
+
+	if (xmc->hwmon_dev) {
+		device_remove_file(xmc->hwmon_dev, &name_attr.dev_attr);
+		sysfs_remove_group(&xmc->hwmon_dev->kobj,
+			&hwmon_xmc_attrgroup);
+		hwmon_device_unregister(xmc->hwmon_dev);
+		xmc->hwmon_dev = NULL;
+	}
+
+	sysfs_remove_group(&pdev->dev.kobj, &xmc_attr_group);
+}
+
+static int mgmt_sysfs_create_xmc(struct platform_device *pdev)
+{
+	struct xocl_xmc *xmc;
+	struct xocl_dev_core *core;
+	int err;
+
+	xmc = platform_get_drvdata(pdev);
+	core = XDEV(xocl_get_xdev(pdev));
+
+	if (!xmc->enabled)
+		return 0;
+
+	err = sysfs_create_group(&pdev->dev.kobj, &xmc_attr_group);
+	if (err) {
+		xocl_err(&pdev->dev, "create xmc attrs failed: 0x%x", err);
+		goto create_attr_failed;
+	}
+	xmc->hwmon_dev = hwmon_device_register(&core->pdev->dev);
+	if (IS_ERR(xmc->hwmon_dev)) {
+		err = PTR_ERR(xmc->hwmon_dev);
+		xocl_err(&pdev->dev, "register xmc hwmon failed: 0x%x", err);
+		goto hwmon_reg_failed;
+	}
+
+	dev_set_drvdata(xmc->hwmon_dev, xmc);
+
+	err = device_create_file(xmc->hwmon_dev, &name_attr.dev_attr);
+	if (err) {
+		xocl_err(&pdev->dev, "create attr name failed: 0x%x", err);
+		goto create_name_failed;
+	}
+
+	err = sysfs_create_group(&xmc->hwmon_dev->kobj,
+		&hwmon_xmc_attrgroup);
+	if (err) {
+		xocl_err(&pdev->dev, "create pw group failed: 0x%x", err);
+		goto create_pw_failed;
+	}
+
+	return 0;
+
+create_pw_failed:
+	device_remove_file(xmc->hwmon_dev, &name_attr.dev_attr);
+create_name_failed:
+	hwmon_device_unregister(xmc->hwmon_dev);
+	xmc->hwmon_dev = NULL;
+hwmon_reg_failed:
+	sysfs_remove_group(&pdev->dev.kobj, &xmc_attr_group);
+create_attr_failed:
+	return err;
+}
+
+static int stop_xmc_nolock(struct platform_device *pdev)
+{
+	struct xocl_xmc *xmc;
+	int retry = 0;
+	u32 reg_val = 0;
+	void *xdev_hdl;
+
+	xmc = platform_get_drvdata(pdev);
+	if (!xmc)
+		return -ENODEV;
+	else if (!xmc->enabled)
+		return -ENODEV;
+
+	xdev_hdl = xocl_get_xdev(xmc->pdev);
+
+	reg_val = READ_GPIO(xmc, 0);
+	xocl_info(&xmc->pdev->dev, "MB Reset GPIO 0x%x", reg_val);
+
+	//Stop XMC and ERT if its currently running
+	if (reg_val == GPIO_ENABLED) {
+		xocl_info(&xmc->pdev->dev,
+			"XMC info, version 0x%x, status 0x%x, id 0x%x",
+			READ_REG32(xmc, XMC_VERSION_REG),
+			READ_REG32(xmc, XMC_STATUS_REG),
+			READ_REG32(xmc, XMC_MAGIC_REG));
+
+		reg_val = READ_REG32(xmc, XMC_STATUS_REG);
+		if (!(reg_val & STATUS_MASK_STOPPED)) {
+			xocl_info(&xmc->pdev->dev, "Stopping XMC...");
+			WRITE_REG32(xmc, CTL_MASK_STOP, XMC_CONTROL_REG);
+			WRITE_REG32(xmc, 1, XMC_STOP_CONFIRM_REG);
+		}
+		//Need to check if ERT is loaded before we attempt to stop it
+		if (!SELF_JUMP(READ_IMAGE_SCHED(xmc, 0))) {
+			reg_val = XOCL_READ_REG32(xmc->base_addrs[IO_CQ]);
+			if (!(reg_val & ERT_STOP_ACK)) {
+				xocl_info(&xmc->pdev->dev, "Stopping scheduler...");
+				XOCL_WRITE_REG32(ERT_STOP_CMD, xmc->base_addrs[IO_CQ]);
+			}
+		}
+
+		retry = 0;
+		while (retry++ < MAX_XMC_RETRY &&
+			!(READ_REG32(xmc, XMC_STATUS_REG) & STATUS_MASK_STOPPED))
+			msleep(RETRY_INTERVAL);
+
+		//Wait for XMC to stop and then check that ERT has also finished
+		if (retry >= MAX_XMC_RETRY) {
+			xocl_err(&xmc->pdev->dev,
+				"Failed to stop XMC");
+			xocl_err(&xmc->pdev->dev,
+				"XMC Error Reg 0x%x",
+				READ_REG32(xmc, XMC_ERROR_REG));
+			xmc->state = XMC_STATE_ERROR;
+			return -ETIMEDOUT;
+		} else if (!SELF_JUMP(READ_IMAGE_SCHED(xmc, 0)) &&
+			 !(XOCL_READ_REG32(xmc->base_addrs[IO_CQ]) & ERT_STOP_ACK)) {
+			while (retry++ < MAX_ERT_RETRY &&
+				!(XOCL_READ_REG32(xmc->base_addrs[IO_CQ]) & ERT_STOP_ACK))
+				msleep(RETRY_INTERVAL);
+			if (retry >= MAX_ERT_RETRY) {
+				xocl_err(&xmc->pdev->dev,
+					"Failed to stop sched");
+				xocl_err(&xmc->pdev->dev,
+					"Scheduler CQ status 0x%x",
+					XOCL_READ_REG32(xmc->base_addrs[IO_CQ]));
+				//We don't exit if ERT doesn't stop since it can hang due to bad kernel
+				//xmc->state = XMC_STATE_ERROR;
+				//return -ETIMEDOUT;
+			}
+		}
+
+		xocl_info(&xmc->pdev->dev, "XMC/sched Stopped, retry %d",
+			retry);
+	}
+
+	// Hold XMC in reset now that its safely stopped
+	xocl_info(&xmc->pdev->dev,
+		"XMC info, version 0x%x, status 0x%x, id 0x%x",
+		READ_REG32(xmc, XMC_VERSION_REG),
+		READ_REG32(xmc, XMC_STATUS_REG),
+		READ_REG32(xmc, XMC_MAGIC_REG));
+	WRITE_GPIO(xmc, GPIO_RESET, 0);
+	xmc->state = XMC_STATE_RESET;
+	reg_val = READ_GPIO(xmc, 0);
+	xocl_info(&xmc->pdev->dev, "MB Reset GPIO 0x%x", reg_val);
+	if (reg_val != GPIO_RESET) {
+		//Shouldnt make it here but if we do then exit
+		xmc->state = XMC_STATE_ERROR;
+		return -EIO;
+	}
+
+	return 0;
+}
+static int stop_xmc(struct platform_device *pdev)
+{
+	struct xocl_xmc *xmc;
+	int ret = 0;
+	void *xdev_hdl;
+
+	xocl_info(&pdev->dev, "Stop Microblaze...");
+	xmc = platform_get_drvdata(pdev);
+	if (!xmc)
+		return -ENODEV;
+	else if (!xmc->enabled)
+		return -ENODEV;
+
+	xdev_hdl = xocl_get_xdev(xmc->pdev);
+
+	mutex_lock(&xmc->xmc_lock);
+	ret = stop_xmc_nolock(pdev);
+	mutex_unlock(&xmc->xmc_lock);
+
+	return ret;
+}
+
+static int load_xmc(struct xocl_xmc *xmc)
+{
+	int retry = 0;
+	u32 reg_val = 0;
+	int ret = 0;
+	void *xdev_hdl;
+
+	if (!xmc->enabled)
+		return -ENODEV;
+
+	mutex_lock(&xmc->xmc_lock);
+
+	/* Stop XMC first */
+	ret = stop_xmc_nolock(xmc->pdev);
+	if (ret != 0)
+		goto out;
+
+	xdev_hdl = xocl_get_xdev(xmc->pdev);
+
+	/* Load XMC and ERT Image */
+	if (xocl_mb_mgmt_on(xdev_hdl)) {
+		xocl_info(&xmc->pdev->dev, "Copying XMC image len %d",
+			xmc->mgmt_binary_length);
+		COPY_MGMT(xmc, xmc->mgmt_binary, xmc->mgmt_binary_length);
+	}
+
+	if (xocl_mb_sched_on(xdev_hdl)) {
+		xocl_info(&xmc->pdev->dev, "Copying scheduler image len %d",
+			xmc->sche_binary_length);
+		COPY_SCHE(xmc, xmc->sche_binary, xmc->sche_binary_length);
+	}
+
+	/* Take XMC and ERT out of reset */
+	WRITE_GPIO(xmc, GPIO_ENABLED, 0);
+	reg_val = READ_GPIO(xmc, 0);
+	xocl_info(&xmc->pdev->dev, "MB Reset GPIO 0x%x", reg_val);
+	if (reg_val != GPIO_ENABLED) {
+		//Shouldnt make it here but if we do then exit
+		xmc->state = XMC_STATE_ERROR;
+		goto out;
+	}
+
+	/* Wait for XMC to start
+	 * Note that ERT will start long before XMC so we don't check anything
+	 */
+	reg_val = READ_REG32(xmc, XMC_STATUS_REG);
+	if (!(reg_val & STATUS_MASK_INIT_DONE)) {
+		xocl_info(&xmc->pdev->dev, "Waiting for XMC to finish init...");
+		retry = 0;
+		while (retry++ < MAX_XMC_RETRY &&
+			!(READ_REG32(xmc, XMC_STATUS_REG) & STATUS_MASK_INIT_DONE))
+			msleep(RETRY_INTERVAL);
+		if (retry >= MAX_XMC_RETRY) {
+			xocl_err(&xmc->pdev->dev,
+				"XMC did not finish init sequence!");
+			xocl_err(&xmc->pdev->dev,
+				"Error Reg 0x%x",
+				READ_REG32(xmc, XMC_ERROR_REG));
+			xocl_err(&xmc->pdev->dev,
+				"Status Reg 0x%x",
+				READ_REG32(xmc, XMC_STATUS_REG));
+			ret = -ETIMEDOUT;
+			xmc->state = XMC_STATE_ERROR;
+			goto out;
+		}
+	}
+	xocl_info(&xmc->pdev->dev, "XMC and scheduler Enabled, retry %d",
+			retry);
+	xocl_info(&xmc->pdev->dev,
+		"XMC info, version 0x%x, status 0x%x, id 0x%x",
+		READ_REG32(xmc, XMC_VERSION_REG),
+		READ_REG32(xmc, XMC_STATUS_REG),
+		READ_REG32(xmc, XMC_MAGIC_REG));
+	xmc->state = XMC_STATE_ENABLED;
+
+	xmc->cap = READ_REG32(xmc, XMC_FEATURE_REG);
+out:
+	mutex_unlock(&xmc->xmc_lock);
+
+	return ret;
+}
+
+static void xmc_reset(struct platform_device *pdev)
+{
+	struct xocl_xmc *xmc;
+
+	xocl_info(&pdev->dev, "Reset Microblaze...");
+	xmc = platform_get_drvdata(pdev);
+	if (!xmc)
+		return;
+
+	load_xmc(xmc);
+}
+
+static int load_mgmt_image(struct platform_device *pdev, const char *image,
+	u32 len)
+{
+	struct xocl_xmc *xmc;
+	char *binary;
+
+	if (len > MAX_IMAGE_LEN)
+		return -EINVAL;
+
+	xmc = platform_get_drvdata(pdev);
+	if (!xmc)
+		return -EINVAL;
+
+	binary = xmc->mgmt_binary;
+	xmc->mgmt_binary = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+	if (!xmc->mgmt_binary)
+		return -ENOMEM;
+
+	if (binary)
+		devm_kfree(&pdev->dev, binary);
+	memcpy(xmc->mgmt_binary, image, len);
+	xmc->mgmt_binary_length = len;
+
+	return 0;
+}
+
+static int load_sche_image(struct platform_device *pdev, const char *image,
+	u32 len)
+{
+	struct xocl_xmc *xmc;
+	char *binary = NULL;
+
+	if (len > MAX_IMAGE_LEN)
+		return -EINVAL;
+
+	xmc = platform_get_drvdata(pdev);
+	if (!xmc)
+		return -EINVAL;
+
+	binary = xmc->sche_binary;
+	xmc->sche_binary = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+	if (!xmc->sche_binary)
+		return -ENOMEM;
+
+	if (binary)
+		devm_kfree(&pdev->dev, binary);
+	memcpy(xmc->sche_binary, image, len);
+	xmc->sche_binary_length = len;
+
+	return 0;
+}
+
+static struct xocl_mb_funcs xmc_ops = {
+	.load_mgmt_image	= load_mgmt_image,
+	.load_sche_image	= load_sche_image,
+	.reset			= xmc_reset,
+	.stop			= stop_xmc,
+	.get_data     = xmc_get_data,
+};
+
+static int xmc_remove(struct platform_device *pdev)
+{
+	struct xocl_xmc *xmc;
+	int	i;
+
+	xmc = platform_get_drvdata(pdev);
+	if (!xmc)
+		return 0;
+
+	if (xmc->mgmt_binary)
+		devm_kfree(&pdev->dev, xmc->mgmt_binary);
+	if (xmc->sche_binary)
+		devm_kfree(&pdev->dev, xmc->sche_binary);
+
+	mgmt_sysfs_destroy_xmc(pdev);
+
+	for (i = 0; i < NUM_IOADDR; i++) {
+		if (xmc->base_addrs[i])
+			iounmap(xmc->base_addrs[i]);
+	}
+
+	mutex_destroy(&xmc->xmc_lock);
+
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(&pdev->dev, xmc);
+
+	return 0;
+}
+
+static int xmc_probe(struct platform_device *pdev)
+{
+	struct xocl_xmc *xmc;
+	struct resource *res;
+	void	*xdev_hdl;
+	int i, err;
+
+	xmc = devm_kzalloc(&pdev->dev, sizeof(*xmc), GFP_KERNEL);
+	if (!xmc) {
+		xocl_err(&pdev->dev, "out of memory");
+		return -ENOMEM;
+	}
+
+	xmc->pdev = pdev;
+	platform_set_drvdata(pdev, xmc);
+
+	xdev_hdl = xocl_get_xdev(pdev);
+	if (xocl_mb_mgmt_on(xdev_hdl) || xocl_mb_sched_on(xdev_hdl)) {
+		xocl_info(&pdev->dev, "Microblaze is supported.");
+		xmc->enabled = true;
+	} else {
+		xocl_err(&pdev->dev, "Microblaze is not supported.");
+		devm_kfree(&pdev->dev, xmc);
+		platform_set_drvdata(pdev, NULL);
+		return 0;
+	}
+
+	for (i = 0; i < NUM_IOADDR; i++) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		if (res) {
+			xocl_info(&pdev->dev, "IO start: 0x%llx, end: 0x%llx",
+				res->start, res->end);
+			xmc->base_addrs[i] =
+				ioremap_nocache(res->start, res->end - res->start + 1);
+			if (!xmc->base_addrs[i]) {
+				err = -EIO;
+				xocl_err(&pdev->dev, "Map iomem failed");
+				goto failed;
+			}
+		} else
+			break;
+	}
+
+	err = mgmt_sysfs_create_xmc(pdev);
+	if (err) {
+		xocl_err(&pdev->dev, "Create sysfs failed, err %d", err);
+		goto failed;
+	}
+
+	xocl_subdev_register(pdev, XOCL_SUBDEV_XMC, &xmc_ops);
+
+	mutex_init(&xmc->xmc_lock);
+
+	return 0;
+
+failed:
+	xmc_remove(pdev);
+	return err;
+}
+
+struct platform_device_id xmc_id_table[] = {
+	{ XOCL_XMC, 0 },
+	{ },
+};
+
+static struct platform_driver	xmc_driver = {
+	.probe		= xmc_probe,
+	.remove		= xmc_remove,
+	.driver		= {
+		.name = XOCL_XMC,
+	},
+	.id_table = xmc_id_table,
+};
+
+int __init xocl_init_xmc(void)
+{
+	return platform_driver_register(&xmc_driver);
+}
+
+void xocl_fini_xmc(void)
+{
+	platform_driver_unregister(&xmc_driver);
+}
diff --git a/drivers/gpu/drm/xocl/subdev/xvc.c b/drivers/gpu/drm/xocl/subdev/xvc.c
new file mode 100644
index 000000000000..355dbad30b00
--- /dev/null
+++ b/drivers/gpu/drm/xocl/subdev/xvc.c
@@ -0,0 +1,461 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2016-2019 Xilinx, Inc. All rights reserved.
+ *
+ * Authors:
+ *
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ":%s: " fmt, __func__
+
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/ioctl.h>
+
+#include "../xocl_drv.h"
+
+/* IOCTL interfaces */
+#define XIL_XVC_MAGIC 0x58564344  // "XVCD"
+#define	MINOR_PUB_HIGH_BIT	0x00000
+#define	MINOR_PRI_HIGH_BIT	0x10000
+#define MINOR_NAME_MASK		0xffffffff
+
+enum xvc_algo_type {
+	XVC_ALGO_NULL,
+	XVC_ALGO_CFG,
+	XVC_ALGO_BAR
+};
+
+struct xil_xvc_ioc {
+	unsigned int opcode;
+	unsigned int length;
+	unsigned char *tms_buf;
+	unsigned char *tdi_buf;
+	unsigned char *tdo_buf;
+};
+
+struct xil_xvc_properties {
+	unsigned int xvc_algo_type;
+	unsigned int config_vsec_id;
+	unsigned int config_vsec_rev;
+	unsigned int bar_index;
+	unsigned int bar_offset;
+};
+
+#define XDMA_IOCXVC	     _IOWR(XIL_XVC_MAGIC, 1, struct xil_xvc_ioc)
+#define XDMA_RDXVC_PROPS _IOR(XIL_XVC_MAGIC, 2, struct xil_xvc_properties)
+
+#define COMPLETION_LOOP_MAX	100
+
+#define XVC_BAR_LENGTH_REG	0x0
+#define XVC_BAR_TMS_REG		0x4
+#define XVC_BAR_TDI_REG		0x8
+#define XVC_BAR_TDO_REG		0xC
+#define XVC_BAR_CTRL_REG	0x10
+
+#define XVC_DEV_NAME "xvc" SUBDEV_SUFFIX
+
+struct xocl_xvc {
+	void *__iomem base;
+	unsigned int instance;
+	struct cdev *sys_cdev;
+	struct device *sys_device;
+};
+
+static dev_t xvc_dev;
+
+static struct xil_xvc_properties xvc_pci_props;
+
+#ifdef __REG_DEBUG__
+/* SECTION: Function definitions */
+static inline void __write_register(const char *fn, u32 value, void *base,
+				unsigned int off)
+{
+	pr_info("%s: 0x%p, W reg 0x%lx, 0x%x.\n", fn, base, off, value);
+	iowrite32(value, base + off);
+}
+
+static inline u32 __read_register(const char *fn, void *base, unsigned int off)
+{
+	u32 v = ioread32(base + off);
+
+	pr_info("%s: 0x%p, R reg 0x%lx, 0x%x.\n", fn, base, off, v);
+	return v;
+}
+#define write_register(v, base, off) __write_register(__func__, v, base, off)
+#define read_register(base, off) __read_register(__func__, base, off)
+
+#else
+#define write_register(v, base, off) iowrite32(v, (base) + (off))
+#define read_register(base, off) ioread32((base) + (off))
+#endif /* #ifdef __REG_DEBUG__ */
+
+
+static int xvc_shift_bits(void *base, u32 tms_bits, u32 tdi_bits,
+			  u32 *tdo_bits)
+{
+	u32 control;
+	u32 write_reg_data;
+	int count;
+
+	/* set tms bit */
+	write_register(tms_bits, base, XVC_BAR_TMS_REG);
+	/* set tdi bits and shift data out */
+	write_register(tdi_bits, base, XVC_BAR_TDI_REG);
+
+	control = read_register(base, XVC_BAR_CTRL_REG);
+	/* enable shift operation */
+	write_reg_data = control | 0x01;
+	write_register(write_reg_data, base, XVC_BAR_CTRL_REG);
+
+	/* poll for completion */
+	count = COMPLETION_LOOP_MAX;
+	while (count) {
+		/* read control reg to check shift operation completion */
+		control = read_register(base, XVC_BAR_CTRL_REG);
+		if ((control & 0x01) == 0)
+			break;
+
+		count--;
+	}
+
+	if (!count)	{
+		pr_warn("XVC bar transaction timed out (0x%0X)\n", control);
+		return -ETIMEDOUT;
+	}
+
+	/* read tdo bits back out */
+	*tdo_bits = read_register(base, XVC_BAR_TDO_REG);
+
+	return 0;
+}
+
+static long xvc_ioctl_helper(struct xocl_xvc *xvc, const void __user *arg)
+{
+	struct xil_xvc_ioc xvc_obj;
+	unsigned int opcode;
+	unsigned int total_bits;
+	unsigned int total_bytes;
+	unsigned int bits, bits_left;
+	unsigned char *buffer = NULL;
+	unsigned char *tms_buf = NULL;
+	unsigned char *tdi_buf = NULL;
+	unsigned char *tdo_buf = NULL;
+	void __iomem *iobase = xvc->base;
+	u32 control_reg_data;
+	u32 write_reg_data;
+	int rv;
+
+	rv = copy_from_user((void *)&xvc_obj, arg,
+				sizeof(struct xil_xvc_ioc));
+	/* anything not copied ? */
+	if (rv) {
+		pr_info("copy_from_user xvc_obj failed: %d.\n", rv);
+		goto cleanup;
+	}
+
+	opcode = xvc_obj.opcode;
+
+	/* Invalid operation type, no operation performed */
+	if (opcode != 0x01 && opcode != 0x02) {
+		pr_info("UNKNOWN opcode 0x%x.\n", opcode);
+		return -EINVAL;
+	}
+
+	total_bits = xvc_obj.length;
+	total_bytes = (total_bits + 7) >> 3;
+
+	buffer = kmalloc(total_bytes * 3, GFP_KERNEL);
+	if (!buffer) {
+		pr_info("OOM %u, op 0x%x, len %u bits, %u bytes.\n",
+			3 * total_bytes, opcode, total_bits, total_bytes);
+		rv = -ENOMEM;
+		goto cleanup;
+	}
+	tms_buf = buffer;
+	tdi_buf = tms_buf + total_bytes;
+	tdo_buf = tdi_buf + total_bytes;
+
+	rv = copy_from_user((void *)tms_buf, xvc_obj.tms_buf, total_bytes);
+	if (rv) {
+		pr_info("copy tmfs_buf failed: %d/%u.\n", rv, total_bytes);
+		goto cleanup;
+	}
+	rv = copy_from_user((void *)tdi_buf, xvc_obj.tdi_buf, total_bytes);
+	if (rv) {
+		pr_info("copy tdi_buf failed: %d/%u.\n", rv, total_bytes);
+		goto cleanup;
+	}
+
+	// If performing loopback test, set loopback bit (0x02) in control reg
+	if (opcode == 0x02) {
+		control_reg_data = read_register(iobase, XVC_BAR_CTRL_REG);
+		write_reg_data = control_reg_data | 0x02;
+		write_register(write_reg_data, iobase, XVC_BAR_CTRL_REG);
+	}
+
+	/* set length register to 32 initially if more than one
+	 * word-transaction is to be done
+	 */
+	if (total_bits >= 32)
+		write_register(0x20, iobase, XVC_BAR_LENGTH_REG);
+
+	for (bits = 0, bits_left = total_bits; bits < total_bits; bits += 32,
+		bits_left -= 32) {
+		unsigned int bytes = bits >> 3;
+		unsigned int shift_bytes = 4;
+		u32 tms_store = 0;
+		u32 tdi_store = 0;
+		u32 tdo_store = 0;
+
+		if (bits_left < 32) {
+			/* set number of bits to shift out */
+			write_register(bits_left, iobase, XVC_BAR_LENGTH_REG);
+			shift_bytes = (bits_left + 7) >> 3;
+		}
+
+		memcpy(&tms_store, tms_buf + bytes, shift_bytes);
+		memcpy(&tdi_store, tdi_buf + bytes, shift_bytes);
+
+		/* Shift data out and copy to output buffer */
+		rv = xvc_shift_bits(iobase, tms_store, tdi_store, &tdo_store);
+		if (rv < 0)
+			goto cleanup;
+
+		memcpy(tdo_buf + bytes, &tdo_store, shift_bytes);
+	}
+
+	// If performing loopback test, reset loopback bit in control reg
+	if (opcode == 0x02) {
+		control_reg_data = read_register(iobase, XVC_BAR_CTRL_REG);
+		write_reg_data = control_reg_data & ~(0x02);
+		write_register(write_reg_data, iobase, XVC_BAR_CTRL_REG);
+	}
+
+	rv = copy_to_user((void *)xvc_obj.tdo_buf, tdo_buf, total_bytes);
+	if (rv) {
+		pr_info("copy back tdo_buf failed: %d/%u.\n", rv, total_bytes);
+		rv = -EFAULT;
+		goto cleanup;
+	}
+
+cleanup:
+	kfree(buffer);
+
+	mmiowb();
+
+	return rv;
+}
+
+static long xvc_read_properties(struct xocl_xvc *xvc, const void __user *arg)
+{
+	int status = 0;
+	struct xil_xvc_properties xvc_props_obj;
+
+	xvc_props_obj.xvc_algo_type   = (unsigned int) xvc_pci_props.xvc_algo_type;
+	xvc_props_obj.config_vsec_id  = xvc_pci_props.config_vsec_id;
+	xvc_props_obj.config_vsec_rev = xvc_pci_props.config_vsec_rev;
+	xvc_props_obj.bar_index		  = xvc_pci_props.bar_index;
+	xvc_props_obj.bar_offset	  = xvc_pci_props.bar_offset;
+
+	if (copy_to_user((void *)arg, &xvc_props_obj, sizeof(xvc_props_obj)))
+		status = -ENOMEM;
+
+	mmiowb();
+	return status;
+}
+
+static long xvc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct xocl_xvc *xvc = filp->private_data;
+	long status = 0;
+
+	switch (cmd) {
+	case XDMA_IOCXVC:
+		status = xvc_ioctl_helper(xvc, (void __user *)arg);
+		break;
+	case XDMA_RDXVC_PROPS:
+		status = xvc_read_properties(xvc, (void __user *)arg);
+		break;
+	default:
+		status = -ENOIOCTLCMD;
+		break;
+	}
+
+	return status;
+}
+
+static int char_open(struct inode *inode, struct file *file)
+{
+	struct xocl_xvc *xvc = NULL;
+
+	xvc = xocl_drvinst_open(inode->i_cdev);
+	if (!xvc)
+		return -ENXIO;
+
+	/* create a reference to our char device in the opened file */
+	file->private_data = xvc;
+	return 0;
+}
+
+/*
+ * Called when the device goes from used to unused.
+ */
+static int char_close(struct inode *inode, struct file *file)
+{
+	struct xocl_xvc *xvc = file->private_data;
+
+	xocl_drvinst_close(xvc);
+	return 0;
+}
+
+
+/*
+ * character device file operations for the XVC
+ */
+static const struct file_operations xvc_fops = {
+	.owner = THIS_MODULE,
+	.open = char_open,
+	.release = char_close,
+	.unlocked_ioctl = xvc_ioctl,
+};
+
+static int xvc_probe(struct platform_device *pdev)
+{
+	struct xocl_xvc *xvc;
+	struct resource *res;
+	struct xocl_dev_core *core;
+	int err;
+
+	xvc = xocl_drvinst_alloc(&pdev->dev, sizeof(*xvc));
+	if (!xvc)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xvc->base = ioremap_nocache(res->start, res->end - res->start + 1);
+	if (!xvc->base) {
+		err = -EIO;
+		xocl_err(&pdev->dev, "Map iomem failed");
+		goto failed;
+	}
+
+	core = xocl_get_xdev(pdev);
+
+	xvc->sys_cdev = cdev_alloc();
+	xvc->sys_cdev->ops = &xvc_fops;
+	xvc->sys_cdev->owner = THIS_MODULE;
+	xvc->instance = XOCL_DEV_ID(core->pdev) |
+		platform_get_device_id(pdev)->driver_data;
+	xvc->sys_cdev->dev = MKDEV(MAJOR(xvc_dev), core->dev_minor);
+	err = cdev_add(xvc->sys_cdev, xvc->sys_cdev->dev, 1);
+	if (err) {
+		xocl_err(&pdev->dev, "cdev_add failed, %d", err);
+		goto failed;
+	}
+
+	xvc->sys_device = device_create(xrt_class, &pdev->dev,
+					xvc->sys_cdev->dev,
+					NULL, "%s%d",
+					platform_get_device_id(pdev)->name,
+					xvc->instance & MINOR_NAME_MASK);
+	if (IS_ERR(xvc->sys_device)) {
+		err = PTR_ERR(xvc->sys_device);
+		goto failed;
+	}
+
+	xocl_drvinst_set_filedev(xvc, xvc->sys_cdev);
+
+	platform_set_drvdata(pdev, xvc);
+	xocl_info(&pdev->dev, "XVC device instance %d initialized\n",
+		xvc->instance);
+
+	// Update PCIe BAR properties in a global structure
+	xvc_pci_props.xvc_algo_type   = XVC_ALGO_BAR;
+	xvc_pci_props.config_vsec_id  = 0;
+	xvc_pci_props.config_vsec_rev = 0;
+	xvc_pci_props.bar_index	      = core->bar_idx;
+	xvc_pci_props.bar_offset      = (unsigned int) res->start - (unsigned int)
+									pci_resource_start(core->pdev, core->bar_idx);
+
+	return 0;
+failed:
+	if (!IS_ERR(xvc->sys_device))
+		device_destroy(xrt_class, xvc->sys_cdev->dev);
+	if (xvc->sys_cdev)
+		cdev_del(xvc->sys_cdev);
+	if (xvc->base)
+		iounmap(xvc->base);
+	xocl_drvinst_free(xvc);
+
+	return err;
+}
+
+
+static int xvc_remove(struct platform_device *pdev)
+{
+	struct xocl_xvc	*xvc;
+
+	xvc = platform_get_drvdata(pdev);
+	if (!xvc) {
+		xocl_err(&pdev->dev, "driver data is NULL");
+		return -EINVAL;
+	}
+	device_destroy(xrt_class, xvc->sys_cdev->dev);
+	cdev_del(xvc->sys_cdev);
+	if (xvc->base)
+		iounmap(xvc->base);
+
+	platform_set_drvdata(pdev, NULL);
+	xocl_drvinst_free(xvc);
+
+	return 0;
+}
+
+struct platform_device_id xvc_id_table[] = {
+	{ XOCL_XVC_PUB, MINOR_PUB_HIGH_BIT },
+	{ XOCL_XVC_PRI, MINOR_PRI_HIGH_BIT },
+	{ },
+};
+
+static struct platform_driver	xvc_driver = {
+	.probe		= xvc_probe,
+	.remove		= xvc_remove,
+	.driver		= {
+		.name = XVC_DEV_NAME,
+	},
+	.id_table = xvc_id_table,
+};
+
+int __init xocl_init_xvc(void)
+{
+	int err = 0;
+
+	err = alloc_chrdev_region(&xvc_dev, 0, XOCL_MAX_DEVICES, XVC_DEV_NAME);
+	if (err < 0)
+		goto err_register_chrdev;
+
+	err = platform_driver_register(&xvc_driver);
+	if (err)
+		goto err_driver_reg;
+	return 0;
+
+err_driver_reg:
+	unregister_chrdev_region(xvc_dev, XOCL_MAX_DEVICES);
+err_register_chrdev:
+	return err;
+}
+
+void xocl_fini_xvc(void)
+{
+	unregister_chrdev_region(xvc_dev, XOCL_MAX_DEVICES);
+	platform_driver_unregister(&xvc_driver);
+}