diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index b116e98..061f229 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ARM_PMU) += arm_pmu.o
 obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
+obj-$(CONFIG_HISI_PMU) += hisilicon/
diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
new file mode 100644
index 0000000..be8f093
--- /dev/null
+++ b/drivers/perf/hisilicon/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HISI_PMU) += djtag.o
diff --git a/drivers/perf/hisilicon/djtag.c b/drivers/perf/hisilicon/djtag.c
new file mode 100644
index 0000000..5aeba55
--- /dev/null
+++ b/drivers/perf/hisilicon/djtag.c
@@ -0,0 +1,731 @@
+/*
+ * Driver for Hisilicon Djtag r/w via System Controller.
+ *
+ * Copyright (C) 2016 Hisilicon Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/time64.h>
+
+#include "djtag.h"
+
+#define SC_DJTAG_TIMEOUT_US    (100 * USEC_PER_MSEC) /* 100ms */
+
+/* for djtag v1 */
+#define SC_DJTAG_MSTR_EN		0x6800
+#define DJTAG_NOR_CFG			BIT(1)	/* accelerate R,W */
+#define DJTAG_MSTR_EN			BIT(0)
+#define SC_DJTAG_MSTR_START_EN		0x6804
+#define DJTAG_MSTR_START_EN		0x1
+#define SC_DJTAG_DEBUG_MODULE_SEL	0x680c
+#define SC_DJTAG_MSTR_WR		0x6810
+#define DJTAG_MSTR_W			0x1
+#define DJTAG_MSTR_R			0x0
+#define SC_DJTAG_CHAIN_UNIT_CFG_EN	0x6814
+#define CHAIN_UNIT_CFG_EN		0xFFFF
+#define SC_DJTAG_MSTR_ADDR		0x6818
+#define SC_DJTAG_MSTR_DATA		0x681c
+#define SC_DJTAG_RD_DATA_BASE		0xe800
+
+/* for djtag v2 */
+#define SC_DJTAG_SEC_ACC_EN_EX		0xd800
+#define DJTAG_SEC_ACC_EN_EX		0x1
+#define SC_DJTAG_MSTR_CFG_EX		0xd818
+#define DJTAG_MSTR_RW_SHIFT_EX		29
+#define DJTAG_MSTR_RD_EX		(0x0 << DJTAG_MSTR_RW_SHIFT_EX)
+#define DJTAG_MSTR_WR_EX		(0x1 << DJTAG_MSTR_RW_SHIFT_EX)
+#define DEBUG_MODULE_SEL_SHIFT_EX	16
+#define CHAIN_UNIT_CFG_EN_EX		0xFFFF
+#define SC_DJTAG_MSTR_ADDR_EX		0xd810
+#define SC_DJTAG_MSTR_DATA_EX		0xd814
+#define SC_DJTAG_MSTR_START_EN_EX	0xd81c
+#define DJTAG_MSTR_START_EN_EX		0x1
+#define SC_DJTAG_RD_DATA_BASE_EX	0xe800
+#define SC_DJTAG_OP_ST_EX		0xe828
+#define DJTAG_OP_DONE_EX		BIT(8)
+
+#define DJTAG_PREFIX "hisi-djtag-dev-"
+
+static DEFINE_IDR(djtag_hosts_idr);
+
+struct hisi_djtag_ops {
+	int (*djtag_read)(void __iomem *regs_base, u32 offset,
+					u32 mod_sel, u32 mod_mask,
+					int chain_id, u32 *rval);
+	int (*djtag_write)(void __iomem *regs_base, u32 offset,
+					u32 mod_sel, u32 mod_mask,
+					u32 wval, int chain_id);
+};
+
+struct hisi_djtag_host {
+	spinlock_t lock;
+	int id;
+	u32 scl_id;
+	struct device dev;
+	struct list_head client_list;
+	void __iomem *sysctl_reg_map;
+	struct device_node *of_node;
+	const struct hisi_djtag_ops *djtag_ops;
+};
+
+#define to_hisi_djtag_client(d) container_of(d, struct hisi_djtag_client, dev)
+#define to_hisi_djtag_driver(d) container_of(d, struct hisi_djtag_driver, \
+								 driver)
+#define MODULE_PREFIX "hisi_djtag:"
+
+static void djtag_prepare_v1(void __iomem *regs_base, u32 offset,
+						u32 mod_sel, u32 mod_mask)
+{
+	/* djtag master enable & accelerate R,W */
+	writel(DJTAG_NOR_CFG | DJTAG_MSTR_EN, regs_base + SC_DJTAG_MSTR_EN);
+
+	/* select module */
+	writel(mod_sel, regs_base + SC_DJTAG_DEBUG_MODULE_SEL);
+	writel(mod_mask & CHAIN_UNIT_CFG_EN,
+			regs_base + SC_DJTAG_CHAIN_UNIT_CFG_EN);
+
+	/* address offset */
+	writel(offset, regs_base + SC_DJTAG_MSTR_ADDR);
+}
+
+static int djtag_do_operation_v1(void __iomem *regs_base)
+{
+	u32 rd;
+	int timeout = SC_DJTAG_TIMEOUT_US;
+
+	/* start to write to djtag register */
+	writel(DJTAG_MSTR_START_EN, regs_base + SC_DJTAG_MSTR_START_EN);
+
+	/* ensure the djtag operation is done */
+	do {
+		rd = readl(regs_base + SC_DJTAG_MSTR_START_EN);
+		if (!(rd & DJTAG_MSTR_EN))
+			break;
+
+		udelay(1);
+	} while (timeout--);
+
+	if (timeout < 0)
+		return -EBUSY;
+
+	return 0;
+}
+
+static void djtag_prepare_v2(void __iomem *regs_base, u32 offset,
+						u32 mod_sel, u32 mod_mask)
+{
+	/* djtag mster enable */
+	writel(DJTAG_SEC_ACC_EN_EX, regs_base + SC_DJTAG_SEC_ACC_EN_EX);
+
+	/* address offset */
+	writel(offset, regs_base + SC_DJTAG_MSTR_ADDR_EX);
+}
+
+static int djtag_do_operation_v2(void __iomem *regs_base)
+{
+	u32 rd;
+	int timeout = SC_DJTAG_TIMEOUT_US;
+
+	/* start to write to djtag register */
+	writel(DJTAG_MSTR_START_EN_EX, regs_base + SC_DJTAG_MSTR_START_EN_EX);
+
+	/* ensure the djtag operation is done */
+	do {
+		rd = readl(regs_base + SC_DJTAG_MSTR_START_EN_EX);
+
+		if (!(rd & DJTAG_MSTR_START_EN_EX))
+			break;
+
+		udelay(1);
+	} while (timeout--);
+
+	if (timeout < 0)
+		goto timeout;
+
+	timeout = SC_DJTAG_TIMEOUT_US;
+	do {
+		rd = readl(regs_base + SC_DJTAG_OP_ST_EX);
+
+		if (rd & DJTAG_OP_DONE_EX)
+			break;
+
+		udelay(1);
+	} while (timeout--);
+
+	if (timeout < 0)
+		goto timeout;
+
+	return 0;
+
+timeout:
+	return -EBUSY;
+}
+
+/*
+ * djtag_read_v1/v2: djtag read interface
+ * @reg_base:	djtag register base address
+ * @offset:	register's offset
+ * @mod_sel:	module selection
+ * @mod_mask:	mask to select specific modules for write
+ * @chain_id:	which sub module for read
+ * @rval:	value in register for read
+ *
+ * Return non-zero if error, else return 0.
+ */
+static int djtag_read_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
+					u32 mod_mask, int chain_id, u32 *rval)
+{
+	int ret;
+
+	if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
+		pr_warn("djtag: do nothing.\n");
+		return 0;
+	}
+
+	djtag_prepare_v1(regs_base, offset, mod_sel, mod_mask);
+
+	writel(DJTAG_MSTR_R, regs_base + SC_DJTAG_MSTR_WR);
+
+	ret = djtag_do_operation_v1(regs_base);
+	if (ret) {
+		if (ret == EBUSY)
+			pr_err("djtag: %s timeout!\n", "read");
+		return ret;
+	}
+
+	*rval = readl(regs_base + SC_DJTAG_RD_DATA_BASE + chain_id * 0x4);
+
+	return 0;
+}
+
+static int djtag_read_v2(void __iomem *regs_base, u32 offset, u32 mod_sel,
+					u32 mod_mask, int chain_id, u32 *rval)
+{
+	int ret;
+
+	if (!(mod_mask & CHAIN_UNIT_CFG_EN_EX)) {
+		pr_warn("djtag: do nothing.\n");
+		return 0;
+	}
+
+	djtag_prepare_v2(regs_base, offset, mod_sel, mod_mask);
+
+	writel(DJTAG_MSTR_RD_EX
+			| (mod_sel << DEBUG_MODULE_SEL_SHIFT_EX)
+			| (mod_mask & CHAIN_UNIT_CFG_EN_EX),
+				regs_base + SC_DJTAG_MSTR_CFG_EX);
+
+	ret = djtag_do_operation_v2(regs_base);
+	if (ret) {
+		if (ret == EBUSY)
+			pr_err("djtag: %s timeout!\n", "read");
+		return ret;
+	}
+
+	*rval = readl(regs_base + SC_DJTAG_RD_DATA_BASE_EX +
+					      chain_id * 0x4);
+
+	return 0;
+}
+
+/*
+ * djtag_write_v1/v2: djtag write interface
+ * @reg_base:	djtag register base address
+ * @offset:	register's offset
+ * @mod_sel:	module selection
+ * @mod_mask:	mask to select specific modules for write
+ * @wval:	value to register for write
+ * @chain_id:	which sub module for read
+ *
+ * Return non-zero if error, else return 0.
+ */
+static int djtag_write_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
+					u32 mod_mask, u32 wval, int chain_id)
+{
+	int ret;
+
+	if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
+		pr_warn("djtag: do nothing.\n");
+		return 0;
+	}
+
+	djtag_prepare_v1(regs_base, offset, mod_sel, mod_mask);
+
+	writel(DJTAG_MSTR_W, regs_base + SC_DJTAG_MSTR_WR);
+	writel(wval, regs_base + SC_DJTAG_MSTR_DATA);
+
+	ret = djtag_do_operation_v1(regs_base);
+	if (ret) {
+		if (ret == EBUSY)
+			pr_err("djtag: %s timeout!\n", "write");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int djtag_write_v2(void __iomem *regs_base, u32 offset, u32 mod_sel,
+					u32 mod_mask, u32 wval, int chain_id)
+{
+	int ret;
+
+	if (!(mod_mask & CHAIN_UNIT_CFG_EN_EX)) {
+		pr_warn("djtag: do nothing.\n");
+		return 0;
+	}
+
+	djtag_prepare_v2(regs_base, offset, mod_sel, mod_mask);
+
+	writel(DJTAG_MSTR_WR_EX
+			| (mod_sel << DEBUG_MODULE_SEL_SHIFT_EX)
+			| (mod_mask & CHAIN_UNIT_CFG_EN_EX),
+				regs_base + SC_DJTAG_MSTR_CFG_EX);
+	writel(wval, regs_base + SC_DJTAG_MSTR_DATA_EX);
+
+	ret = djtag_do_operation_v2(regs_base);
+	if (ret) {
+		if (ret == EBUSY)
+			pr_err("djtag: %s timeout!\n", "write");
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * djtag_writel - write registers via djtag
+ * @client: djtag client handle
+ * @offset:	register's offset
+ * @mod_sel:	module selection
+ * @mod_mask:	mask to select specific modules
+ * @val:	value to write to register
+ *
+ * If error return errno, otherwise return 0.
+ */
+int hisi_djtag_writel(struct hisi_djtag_client *client, u32 offset,
+				 u32 mod_sel, u32 mod_mask, u32 val)
+{
+	void __iomem *reg_map = client->host->sysctl_reg_map;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&client->host->lock, flags);
+	ret = client->host->djtag_ops->djtag_write(reg_map, offset, mod_sel,
+						mod_mask, val, 0);
+	if (ret)
+		pr_err("djtag_writel: error! ret=%d\n", ret);
+	spin_unlock_irqrestore(&client->host->lock, flags);
+
+	return ret;
+}
+
+/**
+ * djtag_readl - read registers via djtag
+ * @client: djtag client handle
+ * @offset:	register's offset
+ * @mod_sel:	module type selection
+ * @chain_id:	chain_id number, mostly is 0
+ * @val:	register's value
+ *
+ * If error return errno, otherwise return 0.
+ */
+int hisi_djtag_readl(struct hisi_djtag_client *client, u32 offset,
+				u32 mod_sel, int chain_id, u32 *val)
+{
+	void __iomem *reg_map = client->host->sysctl_reg_map;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&client->host->lock, flags);
+	ret = client->host->djtag_ops->djtag_read(reg_map, offset, mod_sel,
+					0xffff, chain_id, val);
+	if (ret)
+		pr_err("djtag_readl: error! ret=%d\n", ret);
+	spin_unlock_irqrestore(&client->host->lock, flags);
+
+	return ret;
+}
+
+u32 hisi_djtag_get_sclid(struct hisi_djtag_client *client)
+{
+	return client->host->scl_id;
+}
+
+static const struct hisi_djtag_ops djtag_v1_ops = {
+	.djtag_read  = djtag_read_v1,
+	.djtag_write  = djtag_write_v1,
+};
+
+static const struct hisi_djtag_ops djtag_v2_ops = {
+	.djtag_read  = djtag_read_v2,
+	.djtag_write  = djtag_write_v2,
+};
+
+static const struct of_device_id djtag_of_match[] = {
+	/* for hip05 CPU and IO die */
+	{ .compatible = "hisilicon,hip05-djtag-v1",
+		.data = &djtag_v1_ops },
+	/* for hip06 CPU die */
+	{ .compatible = "hisilicon,hip06-djtag-v1",
+		.data = &djtag_v1_ops },
+	/* for hip06 IO die */
+	{ .compatible = "hisilicon,hip06-djtag-v2",
+		.data = &djtag_v2_ops },
+	/* for hip07 CPU and IO die */
+	{ .compatible = "hisilicon,hip07-djtag-v2",
+		.data = &djtag_v2_ops },
+	{},
+};
+MODULE_DEVICE_TABLE(of, djtag_of_match);
+
+static ssize_t
+show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
+
+	return sprintf(buf, "%s%s\n", MODULE_PREFIX, client->name);
+}
+static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
+
+static struct attribute *hisi_djtag_dev_attrs[] = {
+	NULL,
+	/* modalias helps coldplug:  modprobe $(cat .../modalias) */
+	&dev_attr_modalias.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(hisi_djtag_dev);
+
+static struct device_type hisi_djtag_client_type = {
+	.groups		= hisi_djtag_dev_groups,
+};
+
+static struct hisi_djtag_client *hisi_djtag_verify_client(struct device *dev)
+{
+	return (dev->type == &hisi_djtag_client_type)
+			? to_hisi_djtag_client(dev)
+			: NULL;
+}
+
+static int hisi_djtag_device_probe(struct device *dev)
+{
+	struct hisi_djtag_driver *driver;
+	struct hisi_djtag_client *client;
+	int rc;
+
+	client = hisi_djtag_verify_client(dev);
+	if (!client) {
+		dev_err(dev, "could not find client\n");
+		return -ENODEV;
+	}
+
+	driver = to_hisi_djtag_driver(dev->driver);
+	if (!driver) {
+		dev_err(dev, "could not find driver\n");
+		return -ENODEV;
+	}
+
+	rc = driver->probe(client);
+	if (rc < 0) {
+		dev_err(dev, "client probe failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int hisi_djtag_device_remove(struct device *dev)
+{
+	struct hisi_djtag_driver *driver;
+	struct hisi_djtag_client *client;
+	int rc;
+
+	client = hisi_djtag_verify_client(dev);
+	if (!client) {
+		dev_err(dev, "could not find client\n");
+		return -ENODEV;
+	}
+
+	driver = to_hisi_djtag_driver(dev->driver);
+	if (!driver) {
+		dev_err(dev, "could not find driver\n");
+		return -ENODEV;
+	}
+
+	rc = driver->remove(client);
+	if (rc < 0) {
+		dev_err(dev, "client probe failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int hisi_djtag_device_match(struct device *dev,
+					struct device_driver *drv)
+{
+	struct hisi_djtag_client *client = hisi_djtag_verify_client(dev);
+
+	if (!client)
+		return false;
+
+	if (of_driver_match_device(dev, drv))
+		return true;
+
+	return false;
+}
+
+struct bus_type hisi_djtag_bus = {
+	.name		= "hisi-djtag",
+	.match		= hisi_djtag_device_match,
+	.probe		= hisi_djtag_device_probe,
+	.remove		= hisi_djtag_device_remove,
+};
+
+static struct hisi_djtag_client *
+		hisi_djtag_client_alloc(struct hisi_djtag_host *host)
+{
+	struct hisi_djtag_client *client;
+
+	client = kzalloc(sizeof(*client), GFP_KERNEL);
+	if (!client)
+		return NULL;
+
+	client->host = host;
+
+	client->dev.parent = &client->host->dev;
+	client->dev.bus = &hisi_djtag_bus;
+	client->dev.type = &hisi_djtag_client_type;
+
+	return client;
+}
+
+static int hisi_djtag_new_of_device(struct hisi_djtag_host *host,
+						struct device_node *node)
+{
+	struct hisi_djtag_client *client;
+	int rc;
+
+	client = hisi_djtag_client_alloc(host);
+	if (!client)
+		return -ENOMEM;
+
+	client->dev.of_node = of_node_get(node);
+	snprintf(client->name, DJTAG_CLIENT_NAME_LEN, "%s%d_%s",
+				DJTAG_PREFIX, host->id, node->name);
+	dev_set_name(&client->dev, "%s", client->name);
+
+	rc = device_register(&client->dev);
+	if (rc < 0) {
+		pr_err("DT: error adding new device, rc=%d\n", rc);
+		of_node_put(node);
+		kfree(client);
+		return -EINVAL;
+	}
+
+	list_add(&client->next, &host->client_list);
+	return 0;
+}
+
+static void djtag_register_devices(struct hisi_djtag_host *host)
+{
+	struct device_node *node;
+
+	if (host->of_node) {
+		for_each_available_child_of_node(host->of_node, node) {
+			if (of_node_test_and_set_flag(node, OF_POPULATED))
+				continue;
+			hisi_djtag_new_of_device(host, node);
+		}
+	}
+}
+
+static int hisi_djtag_add_host(struct hisi_djtag_host *host)
+{
+	int rc;
+
+	host->dev.bus = &hisi_djtag_bus;
+
+	rc = idr_alloc(&djtag_hosts_idr, host, 0, 0, GFP_KERNEL);
+	if (rc < 0) {
+		dev_err(&host->dev, "No available djtag host ID'!s\n");
+		return rc;
+	}
+	host->id = rc;
+
+	/* Suffix the unique ID and set djtag hostname */
+	dev_set_name(&host->dev, "djtag-host-%d", host->id);
+	rc = device_register(&host->dev);
+	if (rc < 0) {
+		dev_err(&host->dev, "add_host dev register failed, rc=%d\n",
+									rc);
+		idr_remove(&djtag_hosts_idr, host->id);
+		return rc;
+	}
+
+	djtag_register_devices(host);
+
+	return 0;
+}
+
+static int djtag_host_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct hisi_djtag_host *host;
+	const struct of_device_id *of_id;
+	struct resource *res;
+	int rc;
+
+	host = kzalloc(sizeof(*host), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	of_id = of_match_device(djtag_of_match, dev);
+	if (of_id) {
+		const struct hisi_djtag_ops *djtag_ops = of_id->data;
+
+		host->djtag_ops = djtag_ops;
+		host->of_node = of_node_get(dev->of_node);
+
+		/* Find the SCL ID */
+		if (of_property_read_u32(dev->of_node, "hisi-scl-id",
+					&host->scl_id)) {
+			dev_err(dev, "DT: Cant read scl-id!\n");
+			rc = -EINVAL;
+			goto fail;
+		}
+	} else {
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	spin_lock_init(&host->lock);
+
+	INIT_LIST_HEAD(&host->client_list);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "No reg resorces!\n");
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	if (!resource_size(res)) {
+		dev_err(dev, "Zero reg entry!\n");
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	host->sysctl_reg_map = devm_ioremap_resource(dev, res);
+	if (IS_ERR(host->sysctl_reg_map)) {
+		dev_warn(dev, "Unable to map sysctl registers.\n");
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	platform_set_drvdata(pdev, host);
+
+	rc = hisi_djtag_add_host(host);
+	if (rc) {
+		dev_err(dev, "add host failed, rc=%d\n", rc);
+		goto fail;
+	}
+
+	return 0;
+fail:
+	of_node_put(dev->of_node);
+	kfree(host);
+	return rc;
+}
+
+static int djtag_host_remove(struct platform_device *pdev)
+{
+	struct hisi_djtag_host *host;
+	struct hisi_djtag_client *client, *tmp;
+	struct list_head *client_list;
+
+	host = platform_get_drvdata(pdev);
+	client_list = &host->client_list;
+
+	list_for_each_entry_safe(client, tmp, client_list, next) {
+		list_del(&client->next);
+		device_unregister(&client->dev);
+		of_node_put(client->dev.of_node);
+		kfree(client);
+	}
+
+	device_unregister(&host->dev);
+	idr_remove(&djtag_hosts_idr, host->id);
+	of_node_put(host->of_node);
+	kfree(host);
+
+	return 0;
+}
+
+static struct platform_driver djtag_dev_driver = {
+	.driver = {
+		.name = "hisi-djtag",
+		.of_match_table = djtag_of_match,
+	},
+	.probe = djtag_host_probe,
+	.remove = djtag_host_remove,
+};
+module_platform_driver(djtag_dev_driver);
+
+int hisi_djtag_register_driver(struct module *owner,
+				struct hisi_djtag_driver *driver)
+{
+	int rc;
+
+	driver->driver.owner = owner;
+	driver->driver.bus = &hisi_djtag_bus;
+
+	rc = driver_register(&driver->driver);
+	if (rc < 0)
+		pr_err("%s register failed, rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+void hisi_djtag_unregister_driver(struct hisi_djtag_driver *driver)
+{
+	driver->driver.bus = &hisi_djtag_bus;
+	driver_unregister(&driver->driver);
+}
+
+static int __init hisi_djtag_init(void)
+{
+	int rc;
+
+	rc = bus_register(&hisi_djtag_bus);
+	if (rc) {
+		pr_err("hisi  djtag init failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+module_init(hisi_djtag_init);
+
+static void __exit hisi_djtag_exit(void)
+{
+	bus_unregister(&hisi_djtag_bus);
+}
+module_exit(hisi_djtag_exit);
+
+MODULE_DESCRIPTION("Hisilicon djtag driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
diff --git a/drivers/perf/hisilicon/djtag.h b/drivers/perf/hisilicon/djtag.h
new file mode 100644
index 0000000..53a5462
--- /dev/null
+++ b/drivers/perf/hisilicon/djtag.h
@@ -0,0 +1,39 @@
+/*
+ * Driver for Hisilicon djtag r/w via System Controller.
+ *
+ * Copyright (C) 2016-2017 Hisilicon Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __HISI_DJTAG_H
+#define __HISI_DJTAG_H
+
+#define DJTAG_CLIENT_NAME_LEN 32
+
+struct hisi_djtag_client {
+	struct hisi_djtag_host *host;
+	struct list_head next;
+	char name[DJTAG_CLIENT_NAME_LEN];
+	struct device dev;
+};
+
+struct hisi_djtag_driver {
+	struct device_driver driver;
+	int (*probe)(struct hisi_djtag_client *);
+	int (*remove)(struct hisi_djtag_client *);
+};
+
+extern struct bus_type hisi_djtag_bus;
+
+int hisi_djtag_register_driver(struct module *owner,
+				struct hisi_djtag_driver *driver);
+void hisi_djtag_unregister_driver(struct hisi_djtag_driver *driver);
+int hisi_djtag_readl(struct hisi_djtag_client *client, u32 offset,
+					u32 mod_sel, int chain_id, u32 *val);
+int hisi_djtag_writel(struct hisi_djtag_client *client, u32 offset,
+					u32 mod_sel, u32 mod_mask, u32 val);
+u32 hisi_djtag_get_sclid(struct hisi_djtag_client *client);
+#endif /* __HISI_DJTAG_H */
