diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 8fc851a..9e99275 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -290,6 +290,19 @@ config SCSI_SAS_ATTRS
 	  If you wish to export transport-specific information about
 	  each attached SAS device to sysfs, say Y.
 
+config SCSI_UFS_ATTRS
+	tristate "UFS Transport Attributes"
+	depends on SCSI
+	select BLK_DEV_BSGLIB
+	help
+	  Scsi transport is a framework that allow to send scsi commands to
+	  a non-scsi devices. Still, it is flexible enough to allow sending
+	  non-scsi commands as well. We will use this framework to manage
+	  ufs devices by sending UPIU transactions.
+
+	  If you wish to export transport-specific information about
+	  each attached UFS device to sysfs, say Y.
+
 source "drivers/scsi/libsas/Kconfig"
 
 config SCSI_SRP_ATTRS
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index d1bad43..e0efc06 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_SCSI_ISCSI_ATTRS)	+= scsi_transport_iscsi.o
 obj-$(CONFIG_SCSI_SAS_ATTRS)	+= scsi_transport_sas.o
 obj-$(CONFIG_SCSI_SAS_LIBSAS)	+= libsas/
 obj-$(CONFIG_SCSI_SRP_ATTRS)	+= scsi_transport_srp.o
+obj-$(CONFIG_SCSI_UFS_ATTRS)	+= scsi_transport_ufs.o
 obj-$(CONFIG_SCSI_DH)		+= device_handler/
 
 obj-$(CONFIG_LIBFC)		+= libfc/
diff --git a/drivers/scsi/scsi_transport_ufs.c b/drivers/scsi/scsi_transport_ufs.c
new file mode 100644
index 0000000..62aec49
--- /dev/null
+++ b/drivers/scsi/scsi_transport_ufs.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SCSI UFS transport class
+ *
+ * Copyright (C) 2018 Western Digital Corporation
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/blkdev.h>
+#include <linux/bsg.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_ufs.h>
+
+
+#define UFS_HOST_ATTRS 0
+#define UFS_PORT_ATTRS 0
+
+struct ufs_internal {
+	struct scsi_transport_template t;
+	struct ufs_function_template *f;
+
+	struct device_attribute *host_attrs[UFS_HOST_ATTRS + 1];
+	struct device_attribute *port_attrs[UFS_PORT_ATTRS + 1];
+	struct transport_container port_attr_cont;
+};
+#define to_ufs_internal(tmpl)	container_of(tmpl, struct ufs_internal, t)
+
+struct ufs_host_attrs {
+	atomic_t next_port_id;
+};
+#define to_ufs_host_attrs(x) ((struct ufs_host_attrs *)(x)->shost_data)
+
+static int ufs_bsg_dispatch(struct bsg_job *job)
+{
+	struct Scsi_Host *shost = dev_to_shost(job->dev);
+	struct ufs_internal *i = to_ufs_internal(shost->transportt);
+
+	/* check for payload_len when we'll support data transfer upiu */
+
+	return i->f->bsg_request(job);
+}
+
+static int ufs_bsg_initialize(struct Scsi_Host *shost, struct ufs_port *port)
+{
+	struct request_queue *q;
+	struct ufs_internal *i = to_ufs_internal(shost->transportt);
+
+	if (!i->f->bsg_request) {
+		pr_info("%s can't handle ufs requests\n", shost->hostt->name);
+		return 0;
+	}
+
+	q = bsg_setup_queue(&port->dev, dev_name(&port->dev),
+			    ufs_bsg_dispatch, 0, NULL);
+	if (IS_ERR(q))
+		return PTR_ERR(q);
+	port->q = q;
+
+	return 0;
+}
+
+static int ufs_host_setup(struct transport_container *tc, struct device *dev,
+			  struct device *cdev)
+{
+	struct Scsi_Host *shost = dev_to_shost(dev);
+	struct ufs_host_attrs *ufs_host = to_ufs_host_attrs(shost);
+
+	atomic_set(&ufs_host->next_port_id, 0);
+
+	return 0;
+}
+
+static DECLARE_TRANSPORT_CLASS(ufs_host_class, "ufs_host", ufs_host_setup,
+			       NULL, NULL);
+
+static DECLARE_TRANSPORT_CLASS(ufs_port_class, "ufs_ports",
+			       NULL, NULL, NULL);
+
+static int ufs_host_match(struct attribute_container *cont,
+			  struct device *dev)
+{
+	struct Scsi_Host *shost;
+	struct ufs_internal *i;
+
+	if (!scsi_is_host_device(dev))
+		return 0;
+
+	shost = dev_to_shost(dev);
+	if (!shost->transportt)
+		return 0;
+
+	if (shost->transportt->host_attrs.ac.class != &ufs_host_class.class)
+		return 0;
+
+	i = to_ufs_internal(shost->transportt);
+
+	return &i->t.host_attrs.ac == cont;
+}
+
+static void ufs_port_release(struct device *dev)
+{
+	struct ufs_port *port = dev_to_ufs_port(dev);
+
+	put_device(dev->parent);
+	kfree(port);
+}
+
+static inline int scsi_is_ufs_port(const struct device *dev)
+{
+	return dev->release == ufs_port_release;
+}
+
+static int ufs_port_match(struct attribute_container *cont, struct device *dev)
+{
+	struct Scsi_Host *shost;
+	struct ufs_internal *i;
+
+	if (!scsi_is_ufs_port(dev))
+		return 0;
+
+	shost = dev_to_shost(dev->parent);
+
+	if (!shost->transportt)
+		return 0;
+
+	if (shost->transportt->host_attrs.ac.class != &ufs_host_class.class)
+		return 0;
+
+	i = to_ufs_internal(shost->transportt);
+	return &i->port_attr_cont.ac == cont;
+}
+
+/**
+ * ufs_attach_transport - instantiate UFS transport template
+ * @ft:		UFS transport class function template
+ */
+struct scsi_transport_template *
+ufs_attach_transport(struct ufs_function_template *ft)
+{
+	struct ufs_internal *i = kzalloc(sizeof(struct ufs_internal),
+					GFP_KERNEL);
+
+	if (unlikely(!i))
+		return NULL;
+
+	i->f = ft;
+	i->t.host_attrs.ac.attrs = &i->host_attrs[0];
+	i->t.host_attrs.ac.class = &ufs_host_class.class;
+	i->t.host_attrs.ac.match = ufs_host_match;
+	i->t.host_size = sizeof(struct ufs_host_attrs);
+	i->host_attrs[0] = NULL;
+	transport_container_register(&i->t.host_attrs);
+
+	i->port_attr_cont.ac.class = &ufs_port_class.class;
+	i->port_attr_cont.ac.attrs = &i->port_attrs[0];
+	i->port_attr_cont.ac.match = ufs_port_match;
+	i->port_attrs[0] = NULL;
+	transport_container_register(&i->port_attr_cont);
+
+	return &i->t;
+}
+EXPORT_SYMBOL(ufs_attach_transport);
+
+/**
+ * ufs_release_transport  -  release UFS transport template instance
+ * @t:		transport template instance
+ */
+void ufs_release_transport(struct scsi_transport_template *t)
+{
+	struct ufs_internal *i = to_ufs_internal(t);
+
+	transport_container_unregister(&i->t.host_attrs);
+	transport_container_unregister(&i->port_attr_cont);
+	kfree(i);
+}
+EXPORT_SYMBOL(ufs_release_transport);
+
+/** ufs_port_alloc - allocate and initialize a UFS port structure
+ * @shost:	scsi host that the port is attached to
+ *
+ * Allocates a UFS port structure.  It will be added to the device tree
+ * below the device specified by its Scsi_Host parent.
+ * Returns %NULL on error
+ */
+struct ufs_port *ufs_port_alloc(struct Scsi_Host *shost)
+{
+	struct ufs_port *port;
+	struct device *parent = &shost->shost_gendev;
+	struct ufs_host_attrs *ufs_host = to_ufs_host_attrs(shost);
+
+	port = kzalloc(sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return NULL;
+
+	port->id = atomic_inc_return(&ufs_host->next_port_id);
+
+	device_initialize(&port->dev);
+
+	port->dev.parent = get_device(parent);
+	port->dev.release = ufs_port_release;
+
+	dev_set_name(&port->dev, "ufs-port-%d:%d", shost->host_no, port->id);
+
+	transport_setup_device(&port->dev);
+
+	return port;
+}
+EXPORT_SYMBOL(ufs_port_alloc);
+
+/**
+ * ufs_port_add - add a UFS port to the device hierarchy
+ * @port:	port to be added
+ *
+ * publishes a port to the rest of the system
+ */
+int ufs_port_add(struct ufs_port *port)
+{
+	struct device *dev = &port->dev;
+	struct Scsi_Host *shost = dev_to_shost(dev->parent);
+	int error;
+
+	if (!port) {
+		pr_err("%s failed with null port\n", __func__);
+		return -EINVAL;
+	}
+
+	error = device_add(dev);
+
+	if (error)
+		return error;
+
+	transport_add_device(dev);
+	transport_configure_device(dev);
+
+	if (ufs_bsg_initialize(shost, port))
+		dev_err(dev, "fail to initialize a bsg dev %d\n",
+			shost->host_no);
+
+	return 0;
+}
+EXPORT_SYMBOL(ufs_port_add);
+
+/**
+ * ufs_port_free  -  free a ufs port
+ * @port:	ufs port to free
+ *
+ * Frees the specified ufs port. Should be called on ufs_port_add() failure.
+ */
+void ufs_port_free(struct ufs_port *port)
+{
+	transport_destroy_device(&port->dev);
+	put_device(&port->dev);
+}
+EXPORT_SYMBOL(ufs_port_free);
+
+/**
+ * ufs_port_delete  -  remove a ufs port
+ * @port:	ufs port to remove
+ *
+ */
+void ufs_port_delete(struct ufs_port *port)
+{
+	struct device *dev = &port->dev;
+
+	if (port->q)
+		bsg_unregister_queue(port->q);
+
+	transport_remove_device(dev);
+	device_del(dev);
+	transport_destroy_device(dev);
+
+	put_device(dev);
+}
+EXPORT_SYMBOL(ufs_port_delete);
+
+static int do_ufs_port_del(struct device *dev, void *data)
+{
+	if (scsi_is_ufs_port(dev))
+		ufs_port_delete(dev_to_ufs_port(dev));
+
+	return 0;
+}
+
+/**
+ * ufs_remove_host  -  tear down a ufs port devices
+ * @shost:	the scsi host which is parenting the ufs port objects
+ *
+ * Removes all ufs port devices.
+ * Unharm Other devices that are attached to that host.
+ */
+void ufs_remove_host(struct Scsi_Host *shost)
+{
+	device_for_each_child(&shost->shost_gendev, NULL, do_ufs_port_del);
+}
+EXPORT_SYMBOL_GPL(ufs_remove_host);
+
+static __init int ufs_transport_init(void)
+{
+	int error;
+
+	error = transport_class_register(&ufs_host_class);
+	if (error)
+		goto out;
+
+	error = transport_class_register(&ufs_port_class);
+	if (error)
+		goto out_unregister_transport;
+
+	return 0;
+
+out_unregister_transport:
+	transport_class_unregister(&ufs_host_class);
+out:
+	return error;
+}
+
+static void __exit ufs_transport_exit(void)
+{
+	transport_class_unregister(&ufs_host_class);
+	transport_class_unregister(&ufs_port_class);
+}
+
+MODULE_AUTHOR("WDC");
+MODULE_DESCRIPTION("UFS Transport Attributes");
+MODULE_LICENSE("GPL");
+
+module_init(ufs_transport_init);
+module_exit(ufs_transport_exit);
diff --git a/include/scsi/scsi_transport_ufs.h b/include/scsi/scsi_transport_ufs.h
new file mode 100644
index 0000000..483bce4
--- /dev/null
+++ b/include/scsi/scsi_transport_ufs.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Western Digital Corporation
+ */
+
+#ifndef SCSI_TRANSPORT_UFS_H
+#define SCSI_TRANSPORT_UFS_H
+
+#include <linux/bsg-lib.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+
+struct ufs_function_template {
+	int	(*bsg_request)(struct bsg_job *job);
+};
+
+/* One might expect a more complex structure –
+ * some representation of a list containing all the luns of the systems,
+ * including the w-luns.  However, as we are using upiu transactions that
+ * contains the lun index in its header, we need not to have its representation
+ * in the device tree.
+ */
+struct ufs_port {
+	struct device		dev;
+	int			id;
+	struct request_queue	*q;
+};
+#define dev_to_ufs_port(d) \
+	container_of((d), struct ufs_port, dev)
+
+struct scsi_transport_template *
+ufs_attach_transport(struct ufs_function_template *ft);
+void ufs_release_transport(struct scsi_transport_template *t);
+struct ufs_port *ufs_port_alloc(struct Scsi_Host *shost);
+int ufs_port_add(struct ufs_port *port);
+void ufs_port_free(struct ufs_port *port);
+void ufs_port_delete(struct ufs_port *port);
+void ufs_remove_host(struct Scsi_Host *shost);
+#endif
