diff --git a/Documentation/scsi/ufs.txt b/Documentation/scsi/ufs.txt
index 41a6164..7326974 100644
--- a/Documentation/scsi/ufs.txt
+++ b/Documentation/scsi/ufs.txt
@@ -15,6 +15,8 @@ Contents
   3.2 UTP Transfer requests
   3.3 UFS error handling
   3.4 SCSI Error handling
+4. UFSHCD User Interface
+  4.1 UFS Query IOCTL
 
 
 1. Overview
@@ -125,9 +127,54 @@ The current UFSHCD implementation supports following functionality,
   SCSI Midlayer through .eh_abort_handler, .eh_device_reset_handler and
   .eh_host_reset_handler.
 
-In this version of UFSHCD Query requests and power management
+In this version of UFSHCD power management
 functionality are not implemented.
 
+
+4. UFSHCD User Interface
+------------------
+
+Current impementation of UFSHCD enables user to communicate with
+host and device using IOCTL interface.
+
+4.1 UFS Query IOCTL
+
+  This interface enables user to get information about UFS Device
+  and set some of its configurable properties. You can use following
+  snippet to communicate with this interface:
+
+	#include <sys/ioctl.h>
+	#include <scsi/ufs/ioctl.h>
+	#include <scsi/ufs/ufs.h>
+
+	static int handleQuery(int fd, uint8_t opcode, uint8_t idn,
+		uint8_t buf_size, uint8_t *buffer)
+	{
+		ufs_ioctl_query_data query_data;
+
+		/* Operation code (check <scsi/ufs/ufs.h>) */
+		query_data.opcode = opcode;
+
+		/* Query ID  (check <scsi/ufs/ufs.h>) */
+		query_data.idn = idn;
+
+		/*
+		 * Pointer to allocated memory. Filled with proper data if
+		 * [opcode] determines write operation.
+		 */
+		query_data.buffer = buffer;
+
+		/* Size of allocated memory */
+		query_data.buf_size = buf_size;
+
+		/* [fd] used here shall be opened UFS device */
+		return ioctl(fd, UFS_IOCTL_QUERY, &query_data);
+	}
+
+  This enables user to control some of the UFS specific features inaccessible
+  in other way.
+
+
 UFS Specifications can be found at,
 UFS - http://www.jedec.org/sites/default/files/docs/JESD220.pdf
 UFSHCI - http://www.jedec.org/sites/default/files/docs/JESD223.pdf
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index e27b4d4..7c8618c 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -61,6 +61,18 @@ config SCSI_UFSHCD_PCI
 
 	  If unsure, say N.
 
+config SCSI_UFSHCD_IOCTL
+	bool "Enable ioctl() Configuration Interface for this module"
+	depends on SCSI_UFSHCD
+	---help---
+	This enables UFS Configuration Interface for user-space.
+	Interface enables you to configure and read state of some
+	of the UFS features, which are inavailable in other way.
+
+	  If you intend to configure UFS Host/Device through it, say Y here.
+
+	  If unsure, say N.
+
 config SCSI_UFS_DWC_TC_PCI
 	tristate "DesignWare pci support using a G210 Test Chip"
 	depends on SCSI_UFSHCD_PCI
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 6e77cb0..85bc418 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -1,7 +1,10 @@
 # UFSHCD makefile
+ufshcd-core-y := ufshcd.o
+ufshcd-core-$(CONFIG_SCSI_UFSHCD_IOCTL) += ufshcd-ioctl.o
+
 obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o
 obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o
 obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
-obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
+obj-$(CONFIG_SCSI_UFSHCD) += ufshcd-core.o
 obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
 obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 54deeb7..06da302 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -38,6 +38,7 @@
 
 #include <linux/mutex.h>
 #include <linux/types.h>
+#include <scsi/ufs/ufs.h>
 
 #define MAX_CDB_SIZE	16
 #define GENERAL_UPIU_REQUEST_SIZE 32
@@ -73,6 +74,16 @@ enum {
 	UFS_UPIU_RPMB_WLUN		= 0xC4,
 };
 
+/**
+ * ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor
+ * @lun: LU number to check
+ * @return: true if the lun has a matching unit descriptor, false otherwise
+ */
+static inline bool ufs_is_valid_unit_desc_lun(u8 lun)
+{
+	return (lun == UFS_UPIU_RPMB_WLUN || (lun < UFS_UPIU_MAX_GENERAL_LUN));
+}
+
 /*
  * UFS Protocol Information Unit related definitions
  */
@@ -128,35 +139,6 @@ enum {
 	UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST          = 0x81,
 };
 
-/* Flag idn for Query Requests*/
-enum flag_idn {
-	QUERY_FLAG_IDN_FDEVICEINIT      = 0x01,
-	QUERY_FLAG_IDN_PWR_ON_WPE	= 0x03,
-	QUERY_FLAG_IDN_BKOPS_EN         = 0x04,
-};
-
-/* Attribute idn for Query requests */
-enum attr_idn {
-	QUERY_ATTR_IDN_ACTIVE_ICC_LVL	= 0x03,
-	QUERY_ATTR_IDN_BKOPS_STATUS	= 0x05,
-	QUERY_ATTR_IDN_EE_CONTROL	= 0x0D,
-	QUERY_ATTR_IDN_EE_STATUS	= 0x0E,
-};
-
-/* Descriptor idn for Query requests */
-enum desc_idn {
-	QUERY_DESC_IDN_DEVICE		= 0x0,
-	QUERY_DESC_IDN_CONFIGURATION	= 0x1,
-	QUERY_DESC_IDN_UNIT		= 0x2,
-	QUERY_DESC_IDN_RFU_0		= 0x3,
-	QUERY_DESC_IDN_INTERCONNECT	= 0x4,
-	QUERY_DESC_IDN_STRING		= 0x5,
-	QUERY_DESC_IDN_RFU_1		= 0x6,
-	QUERY_DESC_IDN_GEOMETRY		= 0x7,
-	QUERY_DESC_IDN_POWER		= 0x8,
-	QUERY_DESC_IDN_MAX,
-};
-
 enum desc_header_offset {
 	QUERY_DESC_LENGTH_OFFSET	= 0x00,
 	QUERY_DESC_DESC_TYPE_OFFSET	= 0x01,
@@ -274,19 +256,6 @@ enum bkops_status {
 	BKOPS_STATUS_MAX		 = BKOPS_STATUS_CRITICAL,
 };
 
-/* UTP QUERY Transaction Specific Fields OpCode */
-enum query_opcode {
-	UPIU_QUERY_OPCODE_NOP		= 0x0,
-	UPIU_QUERY_OPCODE_READ_DESC	= 0x1,
-	UPIU_QUERY_OPCODE_WRITE_DESC	= 0x2,
-	UPIU_QUERY_OPCODE_READ_ATTR	= 0x3,
-	UPIU_QUERY_OPCODE_WRITE_ATTR	= 0x4,
-	UPIU_QUERY_OPCODE_READ_FLAG	= 0x5,
-	UPIU_QUERY_OPCODE_SET_FLAG	= 0x6,
-	UPIU_QUERY_OPCODE_CLEAR_FLAG	= 0x7,
-	UPIU_QUERY_OPCODE_TOGGLE_FLAG	= 0x8,
-};
-
 /* Query response result code */
 enum {
 	QUERY_RESULT_SUCCESS                    = 0x00,
diff --git a/drivers/scsi/ufs/ufshcd-ioctl.c b/drivers/scsi/ufs/ufshcd-ioctl.c
new file mode 100644
index 0000000..ae70bf5
--- /dev/null
+++ b/drivers/scsi/ufs/ufshcd-ioctl.c
@@ -0,0 +1,310 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * UFS ioctl - this architecture can be used to configure driver and
+ * device/host parameters, that are otherwise unavailable for such operation
+ */
+
+#include <scsi/ufs/ioctl.h>
+
+#include "ufshcd-ioctl.h"
+
+static int ufshcd_ioctl_query_desc(struct ufs_hba *hba,
+	struct ufs_ioctl_query_data *ioctl_data,
+	void *data_ptr, u8 lun, int length, bool write)
+{
+	int err = 0;
+	u8 index;
+
+	/* LUN indexed descriptors */
+	if (ioctl_data->idn == QUERY_DESC_IDN_UNIT) {
+		if (ufs_is_valid_unit_desc_lun(lun)) {
+			index = lun;
+		} else {
+			err = -EINVAL;
+			goto out;
+		}
+	/* LUN independent descriptors */
+	} else if (ioctl_data->idn < QUERY_DESC_IDN_MAX) {
+		index = 0;
+	/* Invalid descriptors */
+	} else {
+		err = -EINVAL;
+		goto out;
+	}
+
+	err = ufshcd_query_descriptor(hba, ioctl_data->opcode,
+		ioctl_data->idn, index, 0, data_ptr, &length);
+
+	if (!err && !write)
+		ioctl_data->buf_size =
+			min_t(int, ioctl_data->buf_size, length);
+	else
+		ioctl_data->buf_size = 0;
+
+out:
+	if (err)
+		dev_err(hba->dev, "Query Descriptor failed (error: %d)", err);
+
+	return err;
+}
+
+static int ufshcd_ioctl_query_attr(struct ufs_hba *hba,
+	struct ufs_ioctl_query_data *ioctl_data,
+	void *data_ptr, u8 lun, bool write)
+{
+	int err = 0;
+	u8 index;
+
+	/* LUN indexed attributes */
+	if (ioctl_data->idn == QUERY_ATTR_IDN_DYN_CAP_NEEDED ||
+	    ioctl_data->idn == QUERY_ATTR_IDN_PRG_BLK_NUM) {
+		if (ufs_is_valid_unit_desc_lun(lun)) {
+			index = lun;
+		} else {
+			err = -EINVAL;
+			goto out;
+		}
+	/* LUN independent attributes */
+	} else if (ioctl_data->idn < QUERY_ATTR_IDN_MAX) {
+		index = 0;
+	/* Invalid attribiutes */
+	} else {
+		err = -EINVAL;
+		goto out;
+	}
+
+	err = ufshcd_query_attr(hba, ioctl_data->opcode,
+		ioctl_data->idn, index, 0, data_ptr);
+
+	if (!err && !write)
+		ioctl_data->buf_size =
+			min_t(int, ioctl_data->buf_size, sizeof(u32));
+	else
+		ioctl_data->buf_size = 0;
+
+out:
+	if (err)
+		dev_err(hba->dev, "Query Attribute failed (error: %d)", err);
+
+	return err;
+}
+
+static int ufshcd_ioctl_query_flag(struct ufs_hba *hba,
+	struct ufs_ioctl_query_data *ioctl_data, void *data_ptr,
+	bool write)
+{
+	int err = 0;
+
+	/*
+	 * Some flags are added to reserved space between flags in
+	 * more or less recent UFS Specs. If it's reserved for current
+	 * device, we will get an R/W error during operation and return it.
+	 */
+	if (ioctl_data->idn >= QUERY_FLAG_IDN_MAX) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	err = ufshcd_query_flag(hba, ioctl_data->opcode,
+				ioctl_data->idn, data_ptr);
+
+	if (!err && !write)
+		ioctl_data->buf_size =
+			min_t(int, ioctl_data->buf_size, sizeof(bool));
+	else
+		ioctl_data->buf_size = 0;
+
+out:
+	if (err)
+		dev_err(hba->dev, "Query Flag failed (error: %d)", err);
+
+	return err;
+}
+
+/**
+ * ufshcd_query_ioctl - perform user read queries
+ * @hba: per-adapter instance
+ * @lun: used for lun specific queries
+ * @buffer: user space buffer for reading and submitting query data and params
+ *
+ * Returns 0 for success or negative error code otherwise
+ *
+ * Expected/Submitted buffer structure is struct ufs_ioctl_query_data.
+ * It will read the opcode, idn and buf_length parameters, and put the
+ * response in the buffer field while updating the used size in buf_length.
+ */
+static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer)
+{
+	/*
+	 * ioctl_data->buffer is untouchable - it's IO user data ptr.
+	 *
+	 * We might and will copy reply to it or request from it, but it's
+	 * still pointer to user-space data, so treat it as __user argument.
+	 */
+	struct ufs_ioctl_query_data *ioctl_data;
+	int err = 0;
+	void *data_ptr = NULL;
+	int length = 0;
+	size_t data_size = sizeof(*ioctl_data);
+	bool write = false;
+
+	if (!buffer)
+		return -EINVAL;
+
+	/* Prepare space in kernel for users request */
+	ioctl_data = kzalloc(data_size, GFP_KERNEL);
+
+	if (!ioctl_data) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* extract params from user buffer */
+	if (copy_from_user(ioctl_data, buffer, data_size)) {
+		err = -EFAULT;
+		goto out_release_mem;
+	}
+
+	if (!ioctl_data->buf_size) {
+		/* Noting to do */
+		err = 0;
+		goto out_release_mem;
+	}
+
+	switch (ioctl_data->opcode) {
+	case UPIU_QUERY_OPCODE_WRITE_DESC:
+		write = true;
+	case UPIU_QUERY_OPCODE_READ_DESC:
+		ufshcd_map_desc_id_to_length(hba, ioctl_data->idn, &length);
+		break;
+	case UPIU_QUERY_OPCODE_WRITE_ATTR:
+		write = true;
+	case UPIU_QUERY_OPCODE_READ_ATTR:
+		length = sizeof(u32);
+		break;
+	case UPIU_QUERY_OPCODE_SET_FLAG:
+	case UPIU_QUERY_OPCODE_CLEAR_FLAG:
+	case UPIU_QUERY_OPCODE_TOGGLE_FLAG:
+		write = true;
+	case UPIU_QUERY_OPCODE_READ_FLAG:
+		length = sizeof(bool);
+		break;
+	default:
+		length = 0;
+		break;
+	}
+
+	if (!length) {
+		err = -EINVAL;
+		goto out_release_mem;
+	}
+
+	/* We have to allocate memory at this level */
+	data_ptr = kzalloc(length, GFP_KERNEL);
+
+	if (!data_ptr) {
+		err = -ENOMEM;
+		goto out_release_mem;
+	}
+
+	if (write) {
+		length = min_t(int, length, ioctl_data->buf_size);
+		err = copy_from_user(data_ptr, ioctl_data->buffer, length);
+
+		if (err)
+			goto out_release_mem;
+	}
+
+	/* Verify legal parameters & send query */
+	switch (ioctl_data->opcode) {
+	case UPIU_QUERY_OPCODE_WRITE_DESC:
+	case UPIU_QUERY_OPCODE_READ_DESC:
+		err = ufshcd_ioctl_query_desc(hba, ioctl_data, data_ptr,
+					      lun, length, write);
+		break;
+	case UPIU_QUERY_OPCODE_WRITE_ATTR:
+	case UPIU_QUERY_OPCODE_READ_ATTR:
+		err = ufshcd_ioctl_query_attr(hba, ioctl_data, data_ptr,
+					      lun, write);
+		break;
+	case UPIU_QUERY_OPCODE_SET_FLAG:
+	case UPIU_QUERY_OPCODE_CLEAR_FLAG:
+	case UPIU_QUERY_OPCODE_TOGGLE_FLAG:
+	case UPIU_QUERY_OPCODE_READ_FLAG:
+		err = ufshcd_ioctl_query_flag(hba, ioctl_data,
+					      data_ptr, write);
+		break;
+	default:
+		err = -EINVAL;
+		goto out_release_mem;
+	}
+
+	if (err)
+		goto out_release_mem;
+
+	/* Copy basic data to user */
+	err = copy_to_user(buffer, ioctl_data, data_size);
+
+	/*
+	 * We copy result of query to ptr copied
+	 * from user-space in the beginning
+	 * if there is anything to be copied to user.
+	 */
+	if (!err && ioctl_data->buf_size)
+		err = copy_to_user(ioctl_data->buffer, data_ptr,
+			ioctl_data->buf_size);
+
+out_release_mem:
+	kfree(ioctl_data);
+	kfree(data_ptr);
+out:
+	if (err)
+		dev_err(hba->dev, "User Query failed (error: %d)", err);
+
+	return err;
+}
+
+/**
+ * ufshcd_ioctl - ufs ioctl callback registered in scsi_host
+ * @dev: scsi device required for per LUN queries
+ * @cmd: command opcode
+ * @buffer: user space buffer for transferring data
+ *
+ * Supported commands:
+ * UFS_IOCTL_QUERY
+ */
+int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer)
+{
+	struct ufs_hba *hba = shost_priv(dev->host);
+	int err = 0;
+
+	if (WARN_ON(!hba))
+		return -ENODEV;
+
+	switch (cmd) {
+	case UFS_IOCTL_QUERY:
+		pm_runtime_get_sync(hba->dev);
+		err = ufshcd_query_ioctl(hba, ufshcd_scsi_to_upiu_lun(dev->lun),
+				buffer);
+		pm_runtime_put_sync(hba->dev);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	if (err)
+		dev_err(hba->dev, "UFS ioctl() failed (cmd=%04x error: %d)",
+			cmd, err);
+
+	return err;
+}
diff --git a/drivers/scsi/ufs/ufshcd-ioctl.h b/drivers/scsi/ufs/ufshcd-ioctl.h
new file mode 100644
index 0000000..c86257d
--- /dev/null
+++ b/drivers/scsi/ufs/ufshcd-ioctl.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * UFS ioctl - this architecture can be used to configure driver and
+ * device/host parameters, that are otherwise unavailable for such operation
+ */
+
+#ifndef _UFSHCD_IOCTL_H
+#define _UFSHCD_IOCTL_H
+
+#include <linux/errno.h>
+
+#include "ufshcd.h"
+
+#ifdef CONFIG_SCSI_UFSHCD_IOCTL
+int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer);
+#else
+static int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer)
+{
+	return -ENOIOCTLCMD;
+}
+#endif
+
+#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index abc7e87..9f3a39d 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -41,9 +41,11 @@
 #include <linux/devfreq.h>
 #include <linux/nls.h>
 #include <linux/of.h>
+
 #include "ufshcd.h"
 #include "ufs_quirks.h"
 #include "unipro.h"
+#include "ufshcd-ioctl.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/ufs.h>
@@ -2244,7 +2246,7 @@ static int ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
  *
  * Returns UPIU LUN id
  */
-static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun)
+u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun)
 {
 	if (scsi_is_wlun(scsi_lun))
 		return (scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID)
@@ -2259,7 +2261,7 @@ static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun)
  *
  * Returns SCSI W-LUN id
  */
-static inline u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id)
+u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id)
 {
 	return (upiu_wlun_id & ~UFS_UPIU_WLUN_ID) | SCSI_W_LUN_BASE;
 }
@@ -2720,7 +2722,7 @@ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
  *
  * Returns 0 for success, non-zero in case of failure
 */
-static int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
+int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
 			enum attr_idn idn, u8 index, u8 selector, u32 *attr_val)
 {
 	struct ufs_query_req *request = NULL;
@@ -2810,7 +2812,22 @@ static int ufshcd_query_attr_retry(struct ufs_hba *hba,
 	return ret;
 }
 
-static int __ufshcd_query_descriptor(struct ufs_hba *hba,
+/**
+ * ufshcd_query_descriptor - API function for sending descriptor
+ * requests
+ * hba: per-adapter instance
+ * opcode: attribute opcode
+ * idn: attribute idn to access
+ * index: index field
+ * selector: selector field
+ * desc_buf: the buffer that contains the descriptor
+ * buf_len: length parameter passed to the device
+ *
+ * Returns 0 for success, non-zero in case of failure.
+ * The buf_len parameter will contain, on return, the length parameter
+ * received on the response.
+ */
+int ufshcd_query_descriptor(struct ufs_hba *hba,
 			enum query_opcode opcode, enum desc_idn idn, u8 index,
 			u8 selector, u8 *desc_buf, int *buf_len)
 {
@@ -2875,8 +2892,8 @@ static int __ufshcd_query_descriptor(struct ufs_hba *hba,
 }
 
 /**
- * ufshcd_query_descriptor_retry - API function for sending descriptor
- * requests
+ * ufshcd_query_descriptor_retry - function for sending descriptor
+ * requests with retry mechanism
  * hba: per-adapter instance
  * opcode: attribute opcode
  * idn: attribute idn to access
@@ -2890,16 +2907,16 @@ static int __ufshcd_query_descriptor(struct ufs_hba *hba,
  * received on the response.
  */
 static int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
-					 enum query_opcode opcode,
-					 enum desc_idn idn, u8 index,
-					 u8 selector,
-					 u8 *desc_buf, int *buf_len)
+				 enum query_opcode opcode,
+				 enum desc_idn idn, u8 index,
+				 u8 selector,
+				 u8 *desc_buf, int *buf_len)
 {
 	int err;
 	int retries;
 
 	for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
-		err = __ufshcd_query_descriptor(hba, opcode, idn, index,
+		err = ufshcd_query_descriptor(hba, opcode, idn, index,
 						selector, desc_buf, buf_len);
 		if (!err || err == -EINVAL)
 			break;
@@ -3184,7 +3201,7 @@ static inline int ufshcd_read_unit_desc_param(struct ufs_hba *hba,
 	 * Unit descriptors are only available for general purpose LUs (LUN id
 	 * from 0 to 7) and RPMB Well known LU.
 	 */
-	if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN))
+	if (!ufs_is_valid_unit_desc_lun(lun))
 		return -EOPNOTSUPP;
 
 	return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun,
@@ -6502,6 +6519,10 @@ static enum blk_eh_timer_return ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
 	.eh_device_reset_handler = ufshcd_eh_device_reset_handler,
 	.eh_host_reset_handler   = ufshcd_eh_host_reset_handler,
 	.eh_timed_out		= ufshcd_eh_timed_out,
+	.ioctl			= ufshcd_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl		= ufshcd_ioctl,
+#endif
 	.this_id		= -1,
 	.sg_tablesize		= SG_ALL,
 	.cmd_per_lun		= UFSHCD_CMD_PER_LUN,
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index cdc8bd0..bf8a670 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -841,6 +841,13 @@ static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info)
 /* Expose Query-Request API */
 int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
 	enum flag_idn idn, bool *flag_res);
+
+int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
+	enum attr_idn idn, u8 index, u8 selector, u32 *attr_val);
+
+int ufshcd_query_descriptor(struct ufs_hba *hba, enum query_opcode opcode,
+	enum desc_idn idn, u8 index, u8 selector, u8 *desc_buf, int *buf_len);
+
 int ufshcd_hold(struct ufs_hba *hba, bool async);
 void ufshcd_release(struct ufs_hba *hba);
 
@@ -849,6 +856,10 @@ int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
 
 u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba);
 
+/* Expose API for obtaining UPIU/SCSI LUN */
+u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun);
+u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id);
+
 /* Wrapper functions for safely calling variant operations */
 static inline const char *ufshcd_get_var_name(struct ufs_hba *hba)
 {
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h
index a1e1930..f128e9e 100644
--- a/include/scsi/scsi.h
+++ b/include/scsi/scsi.h
@@ -255,6 +255,7 @@ static inline int scsi_is_wlun(u64 lun)
  * Here are some scsi specific ioctl commands which are sometimes useful.
  *
  * Note that include/linux/cdrom.h also defines IOCTL 0x5300 - 0x5395
+ * include/uapi/scsi/ufs/ioctl.h defines 0x53A0
  */
 
 /* Used to obtain PUN and LUN info.  Conflicts with CDROMAUDIOBUFSIZ */
diff --git a/include/uapi/scsi/Kbuild b/include/uapi/scsi/Kbuild
index d791e0a..fad00e0 100644
--- a/include/uapi/scsi/Kbuild
+++ b/include/uapi/scsi/Kbuild
@@ -1,5 +1,6 @@
 # UAPI Header export list
 header-y += fc/
+header-y += ufs/
 header-y += scsi_bsg_fc.h
 header-y += scsi_netlink.h
 header-y += scsi_netlink_fc.h
diff --git a/include/uapi/scsi/ufs/Kbuild b/include/uapi/scsi/ufs/Kbuild
new file mode 100644
index 0000000..cc3ef20
--- /dev/null
+++ b/include/uapi/scsi/ufs/Kbuild
@@ -0,0 +1,3 @@
+# UAPI Header export list
+header-y += ioctl.h
+header-y += ufs.h
diff --git a/include/uapi/scsi/ufs/ioctl.h b/include/uapi/scsi/ufs/ioctl.h
new file mode 100644
index 0000000..1c65505
--- /dev/null
+++ b/include/uapi/scsi/ufs/ioctl.h
@@ -0,0 +1,61 @@
+#ifndef UAPI_UFS_IOCTL_H_
+#define UAPI_UFS_IOCTL_H_
+
+#include <linux/types.h>
+
+/*
+ *  IOCTL opcode for ufs queries has the following opcode after
+ *  SCSI_IOCTL_GET_PCI
+ */
+#define UFS_IOCTL_QUERY			0x53A0
+
+/**
+ * struct ufs_ioctl_query_data - used to transfer data to and from user via
+ * ioctl
+ *
+ * @opcode: type of data to query (descriptor/attribute/flag)
+ * @idn: id of the data structure
+ * @buf_size: number of allocated bytes/data size on return
+ * @buffer: data location
+ *
+ * Received: buffer and buf_size (available space for transferred data)
+ * Submitted: opcode, idn, length, buf_size
+ * Optionally submitted: buffer, buf_size (in Write operations)
+ */
+struct ufs_ioctl_query_data {
+	/*
+	 * User should select one of the opcode defined in "enum query_opcode".
+	 * Please check include/uapi/scsi/ufs/ufs.h for the definition of it.
+	 * Note that only UPIU_QUERY_OPCODE_READ_DESC,
+	 * UPIU_QUERY_OPCODE_READ_ATTR & UPIU_QUERY_OPCODE_READ_FLAG are
+	 * supported as of now. All other query_opcode would be considered
+	 * invalid.
+	 * As of now only read query operations are supported.
+	 */
+	__u32 opcode;
+	/*
+	 * User should select one of the idn from "enum flag_idn" or "enum
+	 * attr_idn" or "enum desc_idn" based on whether opcode above is
+	 * attribute, flag or descriptor.
+	 * Please check include/uapi/scsi/ufs/ufs.h for the definition of it.
+	 */
+	__u8 idn;
+	/*
+	 * User should specify the size of the buffer (buffer[0] below) where
+	 * it wants to read the query data (attribute/flag/descriptor).
+	 * As we might end up reading less data then what is specified in
+	 * buf_size. So we are updating buf_size to what exactly we have read.
+	 */
+	__u16 buf_size;
+	/*
+	 * Pointer to the the data buffer where kernel will copy
+	 * the query data (attribute/flag/descriptor) read from the UFS device
+	 * Note:
+	 * For Read Descriptor you will have to allocate at the most 255 bytes
+	 * For Read Attribute you will have to allocate 4 bytes
+	 * For Read Flag you will have to allocate 1 byte
+	 */
+	__u8 *buffer;
+};
+
+#endif /* UAPI_UFS_IOCTL_H_ */
diff --git a/include/uapi/scsi/ufs/ufs.h b/include/uapi/scsi/ufs/ufs.h
new file mode 100644
index 0000000..096ebea
--- /dev/null
+++ b/include/uapi/scsi/ufs/ufs.h
@@ -0,0 +1,69 @@
+#ifndef UAPI_UFS_H_
+#define UAPI_UFS_H_
+
+/* Flag idn for Query Requests*/
+enum flag_idn {
+	QUERY_FLAG_IDN_FDEVICEINIT		= 0x01,
+	QUERY_FLAG_IDN_PERM_WP_EN		= 0x02,
+	QUERY_FLAG_IDN_PWR_ON_WPE		= 0x03,
+	QUERY_FLAG_IDN_BKOPS_EN			= 0x04,
+	QUERY_FLAG_IDN_DEV_LIFESPAN_MOD_EN	= 0x05,
+	QUERY_FLAG_IDN_PURGE_EN			= 0x06,
+	QUERY_FLAG_IDN_PHY_RES_RM		= 0x08,
+	QUERY_FLAG_IDN_BUSY_RTC			= 0x09,
+	QUERY_FLAG_IDN_PERM_DIS_FW_UPD		= 0x0B,
+	QUERY_FLAG_IDN_MAX,
+};
+
+/* Attribute idn for Query requests */
+enum attr_idn {
+	QUERY_ATTR_IDN_BOOT_LUN_EN		= 0x00,
+	QUERY_ATTR_IDN_RESERVED			= 0x01,
+	QUERY_ATTR_IDN_CUR_PWR_MODE		= 0x02,
+	QUERY_ATTR_IDN_ACTIVE_ICC_LVL		= 0x03,
+	QUERY_ATTR_IDN_OUT_OF_ORD_DATA		= 0x04,
+	QUERY_ATTR_IDN_BKOPS_STATUS		= 0x05,
+	QUERY_ATTR_IDN_PURGE_STATUS		= 0x06,
+	QUERY_ATTR_IDN_MAX_DIN_SIZE		= 0x07,
+	QUERY_ATTR_IDN_MAX_DOUT_SIZE		= 0x08,
+	QUERY_ATTR_IDN_DYN_CAP_NEEDED		= 0x09,
+	QUERY_ATTR_IDN_REF_CLK_FREQ		= 0x0A,
+	QUERY_ATTR_IDN_CONF_DESC_LOCK		= 0x0B,
+	QUERY_ATTR_IDN_MAX_NUM_RTT		= 0x0C,
+	QUERY_ATTR_IDN_EE_CONTROL		= 0x0D,
+	QUERY_ATTR_IDN_EE_STATUS		= 0x0E,
+	QUERY_ATTR_IDN_SEC_PASSED		= 0x0F,
+	QUERY_ATTR_IDN_CTX_CONF			= 0x10,
+	QUERY_ATTR_IDN_PRG_BLK_NUM		= 0x11,
+	QUERY_ATTR_IDN_MAX,
+};
+
+/* Descriptor idn for Query requests */
+enum desc_idn {
+	QUERY_DESC_IDN_DEVICE		= 0x0,
+	QUERY_DESC_IDN_CONFIGURATION	= 0x1,
+	QUERY_DESC_IDN_UNIT		= 0x2,
+	QUERY_DESC_IDN_RFU_0		= 0x3,
+	QUERY_DESC_IDN_INTERCONNECT	= 0x4,
+	QUERY_DESC_IDN_STRING		= 0x5,
+	QUERY_DESC_IDN_RFU_1		= 0x6,
+	QUERY_DESC_IDN_GEOMETRY		= 0x7,
+	QUERY_DESC_IDN_POWER		= 0x8,
+	QUERY_DESC_IDN_DEV_HEALTH	= 0x9,
+	QUERY_DESC_IDN_MAX,
+};
+
+/* UTP QUERY Transaction Specific Fields OpCode */
+enum query_opcode {
+	UPIU_QUERY_OPCODE_NOP		= 0x0,
+	UPIU_QUERY_OPCODE_READ_DESC	= 0x1,
+	UPIU_QUERY_OPCODE_WRITE_DESC	= 0x2,
+	UPIU_QUERY_OPCODE_READ_ATTR	= 0x3,
+	UPIU_QUERY_OPCODE_WRITE_ATTR	= 0x4,
+	UPIU_QUERY_OPCODE_READ_FLAG	= 0x5,
+	UPIU_QUERY_OPCODE_SET_FLAG	= 0x6,
+	UPIU_QUERY_OPCODE_CLEAR_FLAG	= 0x7,
+	UPIU_QUERY_OPCODE_TOGGLE_FLAG	= 0x8,
+};
+
+#endif /* UAPI_UFS_H_ */
