diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 3e3fdae..1af2508 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -321,6 +321,8 @@ Code  Seq#(hex)	Include File		Comments
 0xB0	all	RATIO devices		in development:
 					<mailto:vgo@ratio.de>
 0xB1	00-1F	PPPoX			<mailto:mostrows@styx.uwaterloo.ca>
+0xB2	00-0f	linux/jtag.h		JTAG driver
+					<mailto:oleksandrs@mellanox.com>
 0xB3	00	linux/mmc/ioctl.h
 0xB4	00-0F	linux/gpio.h		<mailto:linux-gpio@vger.kernel.org>
 0xB5	00-0F	uapi/linux/rpmsg.h	<mailto:linux-remoteproc@vger.kernel.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 205d397..141aeaf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7292,6 +7292,14 @@ L:	linux-serial@vger.kernel.org
 S:	Maintained
 F:	drivers/tty/serial/jsm/
 
+JTAG SUBSYSTEM
+M:	Oleksandr Shamray <oleksandrs@mellanox.com>
+M:	Vadim Pasternak <vadimp@mellanox.com>
+S:	Maintained
+F:	include/linux/jtag.h
+F:	include/uapi/linux/jtag.h
+F:	drivers/jtag/
+
 K10TEMP HARDWARE MONITORING DRIVER
 M:	Clemens Ladisch <clemens@ladisch.de>
 L:	linux-hwmon@vger.kernel.org
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 505c676..2214678 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -208,4 +208,6 @@ source "drivers/tee/Kconfig"
 
 source "drivers/mux/Kconfig"
 
+source "drivers/jtag/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index dfdcda0..6a2059b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -182,3 +182,4 @@ obj-$(CONFIG_FPGA)		+= fpga/
 obj-$(CONFIG_FSI)		+= fsi/
 obj-$(CONFIG_TEE)		+= tee/
 obj-$(CONFIG_MULTIPLEXER)	+= mux/
+obj-$(CONFIG_JTAG)		+= jtag/
diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
new file mode 100644
index 0000000..0fad1a3
--- /dev/null
+++ b/drivers/jtag/Kconfig
@@ -0,0 +1,16 @@
+menuconfig JTAG
+	tristate "JTAG support"
+	---help---
+	  This provides basic core functionality support for jtag class devices
+	  Hardware equipped with JTAG microcontroller which can be built
+	  on top of this drivers. Driver exposes the set of IOCTL to the
+	  user space for:
+	  SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
+	  SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
+	  RUNTEST (Forces IEEE 1149.1 bus to a run state for specified
+	  number of clocks).
+
+	  If you want this support, you should say Y here.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called jtag.
diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
new file mode 100644
index 0000000..af37493
--- /dev/null
+++ b/drivers/jtag/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_JTAG)		+= jtag.o
diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c
new file mode 100644
index 0000000..f948d4c
--- /dev/null
+++ b/drivers/jtag/jtag.c
@@ -0,0 +1,312 @@
+/*
+ * drivers/jtag/jtag.c
+ *
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+ *
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/jtag.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <uapi/linux/jtag.h>
+
+struct jtag {
+	struct list_head list;
+	struct device *dev;
+	struct cdev cdev;
+	int id;
+	spinlock_t lock;
+	int open;
+	const struct jtag_ops *ops;
+	unsigned long priv[0] __aligned(ARCH_DMA_MINALIGN);
+};
+
+static dev_t jtag_devt;
+static LIST_HEAD(jtag_list);
+static DEFINE_MUTEX(jtag_mutex);
+static DEFINE_IDA(jtag_ida);
+
+void *jtag_priv(struct jtag *jtag)
+{
+	return jtag->priv;
+}
+EXPORT_SYMBOL_GPL(jtag_priv);
+
+static __u64 jtag_copy_from_user(__u64 udata, unsigned long bit_size)
+{
+	unsigned long size;
+	void *kdata;
+
+	size = DIV_ROUND_UP(bit_size, BITS_PER_BYTE);
+	kdata = memdup_user(u64_to_user_ptr(udata), size);
+
+	return (__u64)(uintptr_t)kdata;
+}
+
+static unsigned long jtag_copy_to_user(__u64 udata, __u64 kdata,
+				       unsigned long bit_size)
+{
+	unsigned long size;
+
+	size = DIV_ROUND_UP(bit_size, BITS_PER_BYTE);
+
+	return copy_to_user(u64_to_user_ptr(udata), jtag_u64_to_ptr(kdata),
+			    size);
+}
+
+static struct class jtag_class = {
+	.name = "jtag",
+	.owner = THIS_MODULE,
+};
+
+static int jtag_run_test_idle_op(struct jtag *jtag,
+				 struct jtag_run_test_idle *idle)
+{
+	if (jtag->ops->idle)
+		return jtag->ops->idle(jtag, idle);
+	else
+		return -EOPNOTSUPP;
+}
+
+static int jtag_xfer_op(struct jtag *jtag, struct jtag_xfer *xfer)
+{
+	if (jtag->ops->xfer)
+		return jtag->ops->xfer(jtag, xfer);
+	else
+		return -EOPNOTSUPP;
+}
+
+static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct jtag *jtag = file->private_data;
+	__u32 *uarg = (__u32 __user *)arg;
+	void  *varg = (void __user *)arg;
+	struct jtag_run_test_idle idle;
+	struct jtag_xfer xfer;
+	__u64 tdio_user;
+	__u32 value;
+	int err;
+
+	switch (cmd) {
+	case JTAG_GIOCFREQ:
+		if (jtag->ops->freq_get)
+			err = jtag->ops->freq_get(jtag, &value);
+		else
+			err = -EOPNOTSUPP;
+		if (err)
+			break;
+
+		err = put_user(value, uarg);
+		break;
+
+	case JTAG_SIOCFREQ:
+		err = __get_user(value, uarg);
+
+		if (value == 0)
+			err = -EINVAL;
+		if (err)
+			break;
+
+		if (jtag->ops->freq_set)
+			err = jtag->ops->freq_set(jtag, value);
+		else
+			err = -EOPNOTSUPP;
+		break;
+
+	case JTAG_IOCRUNTEST:
+		if (copy_from_user(&idle, varg,
+				   sizeof(struct jtag_run_test_idle)))
+			return -ENOMEM;
+		err = jtag_run_test_idle_op(jtag, &idle);
+		break;
+
+	case JTAG_IOCXFER:
+		if (copy_from_user(&xfer, varg, sizeof(struct jtag_xfer)))
+			return -EFAULT;
+
+		if (xfer.length >= JTAG_MAX_XFER_DATA_LEN)
+			return -EFAULT;
+
+		tdio_user = xfer.tdio;
+		xfer.tdio = jtag_copy_from_user(xfer.tdio, xfer.length);
+		if (!xfer.tdio)
+			return -ENOMEM;
+
+		err = jtag_xfer_op(jtag, &xfer);
+		if (jtag_copy_to_user(tdio_user, xfer.tdio, xfer.length)) {
+			kfree(jtag_u64_to_ptr(xfer.tdio));
+			return -EFAULT;
+		}
+
+		kfree(jtag_u64_to_ptr(xfer.tdio));
+		xfer.tdio = tdio_user;
+		if (copy_to_user(varg, &xfer, sizeof(struct jtag_xfer)))
+			return -EFAULT;
+		break;
+
+	case JTAG_GIOCSTATUS:
+		if (jtag->ops->status_get)
+			err = jtag->ops->status_get(jtag, &value);
+		else
+			err = -EOPNOTSUPP;
+		if (err)
+			break;
+
+		err = put_user(value, uarg);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return err;
+}
+
+#ifdef CONFIG_COMPAT
+static long jtag_ioctl_compat(struct file *file, unsigned int cmd,
+			      unsigned long arg)
+{
+	return jtag_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static int jtag_open(struct inode *inode, struct file *file)
+{
+	struct jtag *jtag = container_of(inode->i_cdev, struct jtag, cdev);
+
+	spin_lock(&jtag->lock);
+
+	if (jtag->open) {
+		dev_info(NULL, "jtag already opened\n");
+		spin_unlock(&jtag->lock);
+		return -EBUSY;
+	}
+
+	jtag->open++;
+	file->private_data = jtag;
+	spin_unlock(&jtag->lock);
+	return 0;
+}
+
+static int jtag_release(struct inode *inode, struct file *file)
+{
+	struct jtag *jtag = file->private_data;
+
+	spin_lock(&jtag->lock);
+	jtag->open--;
+	spin_unlock(&jtag->lock);
+	return 0;
+}
+
+static const struct file_operations jtag_fops = {
+	.owner		= THIS_MODULE,
+	.open		= jtag_open,
+	.release	= jtag_release,
+	.llseek		= noop_llseek,
+	.unlocked_ioctl = jtag_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= jtag_ioctl_compat,
+#endif
+};
+
+struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops)
+{
+	struct jtag *jtag;
+
+	jtag = kzalloc(sizeof(*jtag) + round_up(priv_size, ARCH_DMA_MINALIGN),
+		       GFP_KERNEL);
+	if (!jtag)
+		return NULL;
+
+	jtag->ops = ops;
+	return jtag;
+}
+EXPORT_SYMBOL_GPL(jtag_alloc);
+
+void jtag_free(struct jtag *jtag)
+{
+	kfree(jtag);
+}
+EXPORT_SYMBOL_GPL(jtag_free);
+
+int jtag_register(struct jtag *jtag)
+{
+	int id;
+	int err;
+
+	id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL);
+	if (id < 0)
+		return id;
+
+	jtag->id = id;
+	cdev_init(&jtag->cdev, &jtag_fops);
+	jtag->cdev.owner = THIS_MODULE;
+	err = cdev_add(&jtag->cdev, MKDEV(MAJOR(jtag_devt), jtag->id), 1);
+	if (err)
+		goto err_cdev;
+
+	/* Register this jtag device with the driver core */
+	jtag->dev = device_create(&jtag_class, NULL, MKDEV(MAJOR(jtag_devt),
+							   jtag->id),
+				  NULL, "jtag%d", jtag->id);
+	if (!jtag->dev)
+		goto err_device_create;
+
+	jtag->open = 0;
+	dev_set_drvdata(jtag->dev, jtag);
+	spin_lock_init(&jtag->lock);
+	mutex_lock(&jtag_mutex);
+	list_add_tail(&jtag->list, &jtag_list);
+	mutex_unlock(&jtag_mutex);
+	return err;
+
+err_device_create:
+	cdev_del(&jtag->cdev);
+err_cdev:
+	ida_simple_remove(&jtag_ida, id);
+	return err;
+}
+EXPORT_SYMBOL_GPL(jtag_register);
+
+void jtag_unregister(struct jtag *jtag)
+{
+	struct device *dev = jtag->dev;
+
+	mutex_lock(&jtag_mutex);
+	list_del(&jtag->list);
+	mutex_unlock(&jtag_mutex);
+	cdev_del(&jtag->cdev);
+	device_unregister(dev);
+	ida_simple_remove(&jtag_ida, jtag->id);
+}
+EXPORT_SYMBOL_GPL(jtag_unregister);
+
+static int __init jtag_init(void)
+{
+	int err;
+
+	err = alloc_chrdev_region(&jtag_devt, 0, 1, "jtag");
+	if (err)
+		return err;
+	return class_register(&jtag_class);
+}
+
+static void __exit jtag_exit(void)
+{
+	class_unregister(&jtag_class);
+}
+
+module_init(jtag_init);
+module_exit(jtag_exit);
+
+MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
+MODULE_DESCRIPTION("Generic jtag support");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/jtag.h b/include/linux/jtag.h
new file mode 100644
index 0000000..f48ae9d
--- /dev/null
+++ b/include/linux/jtag.h
@@ -0,0 +1,48 @@
+/*
+ * drivers/jtag/jtag.c
+ *
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+ *
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef __JTAG_H
+#define __JTAG_H
+
+#include <uapi/linux/jtag.h>
+
+#ifndef ARCH_DMA_MINALIGN
+#define ARCH_DMA_MINALIGN 1
+#endif
+
+#define jtag_u64_to_ptr(arg) ((void *)(uintptr_t)arg)
+
+#define JTAG_MAX_XFER_DATA_LEN 65535
+
+struct jtag;
+/**
+ * struct jtag_ops - callbacks for jtag control functions:
+ *
+ * @freq_get: get frequency function. Filled by device driver
+ * @freq_set: set frequency function. Filled by device driver
+ * @status_get: set status function. Filled by device driver
+ * @idle: set JTAG to idle state function. Filled by device driver
+ * @xfer: send JTAG xfer function. Filled by device driver
+ */
+struct jtag_ops {
+	int (*freq_get)(struct jtag *jtag, __u32 *freq);
+	int (*freq_set)(struct jtag *jtag, __u32 freq);
+	int (*status_get)(struct jtag *jtag, __u32 *state);
+	int (*idle)(struct jtag *jtag, struct jtag_run_test_idle *idle);
+	int (*xfer)(struct jtag *jtag, struct jtag_xfer *xfer);
+};
+
+void *jtag_priv(struct jtag *jtag);
+int jtag_register(struct jtag *jtag);
+void jtag_unregister(struct jtag *jtag);
+struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops);
+void jtag_free(struct jtag *jtag);
+
+#endif /* __JTAG_H */
diff --git a/include/uapi/linux/jtag.h b/include/uapi/linux/jtag.h
new file mode 100644
index 0000000..af22ea6
--- /dev/null
+++ b/include/uapi/linux/jtag.h
@@ -0,0 +1,111 @@
+/*
+ * JTAG class driver
+ *
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+ *
+ * Released under the GPLv2/BSD.
+ * SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+ */
+
+#ifndef __UAPI_LINUX_JTAG_H
+#define __UAPI_LINUX_JTAG_H
+
+/**
+ * enum jtag_xfer_mode:
+ *
+ * @JTAG_XFER_HW_MODE: hardware mode transfer
+ * @JTAG_XFER_SW_MODE: software mode transfer
+ */
+enum jtag_xfer_mode {
+	JTAG_XFER_HW_MODE,
+	JTAG_XFER_SW_MODE,
+};
+
+/**
+ * enum jtag_endstate:
+ *
+ * @JTAG_STATE_IDLE: JTAG state machine IDLE state
+ * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state
+ * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state
+ */
+enum jtag_endstate {
+	JTAG_STATE_IDLE,
+	JTAG_STATE_PAUSEIR,
+	JTAG_STATE_PAUSEDR,
+};
+
+/**
+ * enum jtag_xfer_type:
+ *
+ * @JTAG_SIR_XFER: SIR transfer
+ * @JTAG_SDR_XFER: SDR transfer
+ */
+enum jtag_xfer_type {
+	JTAG_SIR_XFER,
+	JTAG_SDR_XFER,
+};
+
+/**
+ * enum jtag_xfer_direction:
+ *
+ * @JTAG_READ_XFER: read transfer
+ * @JTAG_WRITE_XFER: write transfer
+ */
+enum jtag_xfer_direction {
+	JTAG_READ_XFER,
+	JTAG_WRITE_XFER,
+};
+
+/**
+ * struct jtag_run_test_idle - forces JTAG state machine to
+ * RUN_TEST/IDLE state
+ *
+ * @mode: access mode
+ * @reset: 0 - run IDLE/PAUSE from current state
+ *         1 - go through TEST_LOGIC/RESET state before  IDLE/PAUSE
+ * @end: completion flag
+ * @tck: clock counter
+ *
+ * Structure represents interface to JTAG device for jtag idle
+ * execution.
+ */
+struct jtag_run_test_idle {
+	__u8	mode;
+	__u8	reset;
+	__u8	endstate;
+	__u8	tck;
+};
+
+/**
+ * struct jtag_xfer - jtag xfer:
+ *
+ * @mode: access mode
+ * @type: transfer type
+ * @direction: xfer direction
+ * @length: xfer bits len
+ * @tdio : xfer data array
+ * @endir: xfer end state
+ *
+ * Structure represents interface to Aspeed JTAG device for jtag sdr xfer
+ * execution.
+ */
+struct jtag_xfer {
+	__u8	mode;
+	__u8	type;
+	__u8	direction;
+	__u8	endstate;
+	__u32	length;
+	__u64	tdio;
+};
+
+#define __JTAG_IOCTL_MAGIC	0xb2
+
+#define JTAG_IOCRUNTEST	_IOW(__JTAG_IOCTL_MAGIC, 0,\
+			     struct jtag_run_test_idle)
+#define JTAG_SIOCFREQ	_IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int)
+#define JTAG_GIOCFREQ	_IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int)
+#define JTAG_IOCXFER	_IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer)
+#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum jtag_endstate)
+
+#endif /* __UAPI_LINUX_JTAG_H */
