linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V2 0/9] ufs patch siries
@ 2013-05-14  7:08 Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 1/9] scsi: ufs: add support for query requests Dolev Raviv
                   ` (8 more replies)
  0 siblings, 9 replies; 12+ messages in thread
From: Dolev Raviv @ 2013-05-14  7:08 UTC (permalink / raw)
  To: linux-scsi; +Cc: linux-arm-msm, Dolev Raviv

This patch series clusters the latest version of all the UFS patches in the
SCSI mailing list. It gives a stable functional base line for the UFS driver.

It includes the following versions:
> [PATCH V5] scsi: ufs: add support for query requests
> [PATCH 1/2] Documentation: devicetree: Add DT bindings for UFS host
> controller
> [PATCH 2/2] scsi: ufs: Fix the response UPIU length setting
> [PATCH v4 1/6] scsi: ufs: wrap the i/o access operations
> [PATCH v4 2/6] scsi: ufs: amend interrupt configuration
> [PATCH v4 3/6] scsi: ufs: fix interrupt status clears
> [PATCH v4 4/6] scsi: ufs: rework link start-up process
> [PATCH V5 1/1] scsi: ufs: Add support for sending NOP OUT UPIU
> [PATCH V4] scsi: ufs: Set fDeviceInit flag to initiate device
> initialization

But does not include:
> [PATCH v4 5/6] scsi: ufs: add dme configuration primit
> [PATCH v4 6/6] scsi: ufs: add dme control primitives

-- 
V2:
- Add query requst support patch which was ommited from the merge window

Dolev Raviv (9):
  scsi: ufs: add support for query requests
  Documentation: devicetree: Add DT bindings for UFS host controller
  scsi: ufs: Fix the response UPIU length setting
  scsi: ufs: wrap the i/o access operations
  scsi: ufs: amend interrupt configuration
  scsi: ufs: fix interrupt status clears
  scsi: ufs: rework link start-up process
  scsi: ufs: Add support for sending NOP OUT UPIU
  scsi: ufs: Set fDeviceInit flag to initiate device initialization

 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt      |   16 +
 drivers/scsi/ufs/ufs.h                             |  132 ++-
 drivers/scsi/ufs/ufshcd.c                          | 1172 +++++++++++++++-----
 drivers/scsi/ufs/ufshcd.h                          |   47 +-
 drivers/scsi/ufs/ufshci.h                          |    7 +-
 5 files changed, 1101 insertions(+), 273 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt

-- 
1.7.6

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH V2 1/9] scsi: ufs: add support for query requests
  2013-05-14  7:08 [PATCH V2 0/9] ufs patch siries Dolev Raviv
@ 2013-05-14  7:08 ` Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 2/9] Documentation: devicetree: Add DT bindings for UFS host controller Dolev Raviv
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Dolev Raviv @ 2013-05-14  7:08 UTC (permalink / raw)
  To: linux-scsi; +Cc: linux-arm-msm, Dolev Raviv, Santosh Y, open list

Add support for sending UFS query requests through tagged command
queuing. This design allows queuing query requests in any open slot
along with other SCSI commands. In this way there is no need to
save a slot in the requests queue and decrease its size.

A query request is posing to a SCSI command to use native flow. But
unlike normal SCSI command flow, the data and response fields are
filled in UFS host data structure instead of passing as arguments
while queuing into SCSI mlqueue (mid-layer SCSI queue, the requests
from this queue are submitted to the device queue). As per specification
only one query request is allowed to be processed by device. Hence a
mutex lock for protecting data and response fields (hba->query.request and
hba->query.response) needs to be held while query request is in
progress.

The API for submitting a query request is ufs_query_request() in
ufshcd.c. This function is responsible for:
1. Obtaining the SCSI device from the host
2. Keeping the query mutex to prevent multiple queries
3. Storing the required data for sending a query request in ufs_hba
4. Queuing a SCSI vendor specific command to trigger a query request
   in the UFS driver.

The callers of ufs_query_request() are expected to fill the query
command data fields and are to provide an allocated response field
for the driver to fill response fields after request completion.

The request and response upiu is extended in a union to enable using the
same data structure, both for command upiu and query request upiu.

The query request flow is separated from the scsi command flow in:
1. Preparing the request
2. Validating response (error) codes
3. Copying data (only used for descriptor read/write query requests)
4. Copying response/sense

Data error can't be handled in the scsi command native flow. Hence,
we pass the code as without a change back to the submitting layer.

UPIU (UFS Protocol Information Unit) size is increased to 512 bytes
from 128. The UPIU header and the transaction specific fields (SCSI
command or Query Request OSF - Op-code Specific Fields) are 32 bytes
together, the rest is used to transfer extra request data (such as
descriptor in query requests). In order to accommodate the largest
descriptor in the UFS spec (256 bytes) we need to increase the UPIU
size.

Signed-off-by: Dolev Raviv <draviv@codeaurora.org>
Signed-off-by: Santosh Y <santoshsy@gmail.com>

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 139bc06..086ff03 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -36,10 +36,20 @@
 #ifndef _UFS_H
 #define _UFS_H
 
+#include <linux/mutex.h>
+#include <linux/types.h>
+
 #define MAX_CDB_SIZE	16
+#define GENERAL_UPIU_REQUEST_SIZE 32
+#define UPIU_HEADER_DATA_SEGMENT_MAX_SIZE	((ALIGNED_UPIU_SIZE) - \
+					(GENERAL_UPIU_REQUEST_SIZE))
+#define QUERY_OSF_SIZE			((GENERAL_UPIU_REQUEST_SIZE) - \
+					(sizeof(struct utp_upiu_header)))
+#define UFS_QUERY_RESERVED_SCSI_CMD 0xCC
+#define UFS_QUERY_CMD_SIZE 10
 
 #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
-			((byte3 << 24) | (byte2 << 16) |\
+			cpu_to_be32((byte3 << 24) | (byte2 << 16) |\
 			 (byte1 << 8) | (byte0))
 
 /*
@@ -62,7 +72,7 @@ enum {
 	UPIU_TRANSACTION_COMMAND	= 0x01,
 	UPIU_TRANSACTION_DATA_OUT	= 0x02,
 	UPIU_TRANSACTION_TASK_REQ	= 0x04,
-	UPIU_TRANSACTION_QUERY_REQ	= 0x26,
+	UPIU_TRANSACTION_QUERY_REQ	= 0x16,
 };
 
 /* UTP UPIU Transaction Codes Target to Initiator */
@@ -73,6 +83,7 @@ enum {
 	UPIU_TRANSACTION_TASK_RSP	= 0x24,
 	UPIU_TRANSACTION_READY_XFER	= 0x31,
 	UPIU_TRANSACTION_QUERY_RSP	= 0x36,
+	UPIU_TRANSACTION_REJECT_UPIU	= 0x3F,
 };
 
 /* UPIU Read/Write flags */
@@ -90,6 +101,12 @@ enum {
 	UPIU_TASK_ATTR_ACA	= 0x03,
 };
 
+/* UPIU Query request function */
+enum {
+	UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01,
+	UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81,
+};
+
 /* UTP QUERY Transaction Specific Fields OpCode */
 enum {
 	UPIU_QUERY_OPCODE_NOP		= 0x0,
@@ -103,6 +120,21 @@ enum {
 	UPIU_QUERY_OPCODE_TOGGLE_FLAG	= 0x8,
 };
 
+/* Query response result code */
+enum {
+	QUERY_RESULT_SUCCESS			= 0x00,
+	QUERY_RESULT_NOT_READABLE		= 0xF6,
+	QUERY_RESULT_NOT_WRITEABLE		= 0xF7,
+	QUERY_RESULT_ALREADY_WRITTEN		= 0xF8,
+	QUERY_RESULT_INVALID_LENGTH		= 0xF9,
+	QUERY_RESULT_INVALID_VALUE		= 0xFA,
+	QUERY_RESULT_INVALID_SELECTOR		= 0xFB,
+	QUERY_RESULT_INVALID_INDEX		= 0xFC,
+	QUERY_RESULT_INVALID_IDN		= 0xFD,
+	QUERY_RESULT_INVALID_OPCODE		= 0xFE,
+	QUERY_RESULT_GENERAL_FAILURE		= 0xFF,
+};
+
 /* UTP Transfer Request Command Type (CT) */
 enum {
 	UPIU_COMMAND_SET_TYPE_SCSI	= 0x0,
@@ -110,10 +142,17 @@ enum {
 	UPIU_COMMAND_SET_TYPE_QUERY	= 0x2,
 };
 
+/* UTP Transfer Request Command Offset */
+#define UPIU_COMMAND_TYPE_OFFSET	28
+
+/* Offset of the response code in the UPIU header */
+#define UPIU_RSP_CODE_OFFSET		8
+
 enum {
 	MASK_SCSI_STATUS	= 0xFF,
 	MASK_TASK_RESPONSE	= 0xFF00,
 	MASK_RSP_UPIU_RESULT	= 0xFFFF,
+	MASK_QUERY_DATA_SEG_LEN	= 0xFFFF,
 };
 
 /* Task management service response */
@@ -138,26 +177,59 @@ struct utp_upiu_header {
 
 /**
  * struct utp_upiu_cmd - Command UPIU structure
- * @header: UPIU header structure DW-0 to DW-2
  * @data_transfer_len: Data Transfer Length DW-3
  * @cdb: Command Descriptor Block CDB DW-4 to DW-7
  */
 struct utp_upiu_cmd {
-	struct utp_upiu_header header;
 	u32 exp_data_transfer_len;
 	u8 cdb[MAX_CDB_SIZE];
 };
 
 /**
- * struct utp_upiu_rsp - Response UPIU structure
- * @header: UPIU header DW-0 to DW-2
+ * struct utp_upiu_query - upiu request buffer structure for
+ * query request.
+ * @opcode: command to perform B-0
+ * @idn: a value that indicates the particular type of data B-1
+ * @index: Index to further identify data B-2
+ * @selector: Index to further identify data B-3
+ * @reserved_osf: spec reserved field B-4,5
+ * @length: number of descriptor bytes to read/write B-6,7
+ * @value: Attribute value to be written DW-6
+ * @reserved: spec reserved DW-7,8
+ */
+struct utp_upiu_query {
+	u8 opcode;
+	u8 idn;
+	u8 index;
+	u8 selector;
+	u16 reserved_osf;
+	u16 length;
+	u32 value;
+	u32 reserved[2];
+};
+
+/**
+ * struct utp_upiu_req - general upiu request structure
+ * @header:UPIU header structure DW-0 to DW-2
+ * @sc: fields structure for scsi command
+ * @qr: fields structure for query request
+ */
+struct utp_upiu_req {
+	struct utp_upiu_header header;
+	union {
+		struct utp_upiu_cmd sc;
+		struct utp_upiu_query qr;
+	};
+};
+
+/**
+ * struct utp_cmd_rsp - Response UPIU structure
  * @residual_transfer_count: Residual transfer count DW-3
  * @reserved: Reserved double words DW-4 to DW-7
  * @sense_data_len: Sense data length DW-8 U16
  * @sense_data: Sense data field DW-8 to DW-12
  */
-struct utp_upiu_rsp {
-	struct utp_upiu_header header;
+struct utp_cmd_rsp {
 	u32 residual_transfer_count;
 	u32 reserved[4];
 	u16 sense_data_len;
@@ -165,6 +237,20 @@ struct utp_upiu_rsp {
 };
 
 /**
+ * struct utp_upiu_rsp - general upiu response structure
+ * @header: UPIU header structure DW-0 to DW-2
+ * @sc: fields structure for scsi command
+ * @qr: fields structure for query request
+ */
+struct utp_upiu_rsp {
+	struct utp_upiu_header header;
+	union {
+		struct utp_cmd_rsp sc;
+		struct utp_upiu_query qr;
+	};
+};
+
+/**
  * struct utp_upiu_task_req - Task request UPIU structure
  * @header - UPIU header structure DW0 to DW-2
  * @input_param1: Input parameter 1 DW-3
@@ -194,4 +280,24 @@ struct utp_upiu_task_rsp {
 	u32 reserved[3];
 };
 
+/**
+ * struct ufs_query_req - parameters for building a query request
+ * @query_func: UPIU header query function
+ * @upiu_req: the query request data
+ */
+struct ufs_query_req {
+	u8 query_func;
+	struct utp_upiu_query upiu_req;
+};
+
+/**
+ * struct ufs_query_resp - UPIU QUERY
+ * @response: device response code
+ * @upiu_res: query response data
+ */
+struct ufs_query_res {
+	u8 response;
+	struct utp_upiu_query upiu_res;
+};
+
 #endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c32a478..4ddc8be 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -202,18 +202,13 @@ static inline void ufshcd_free_hba_memory(struct ufs_hba *hba)
 }
 
 /**
- * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
+ * ufshcd_get_req_rsp - returns the TR response
  * @ucd_rsp_ptr: pointer to response UPIU
- *
- * This function checks the response UPIU for valid transaction type in
- * response field
- * Returns 0 on success, non-zero on failure
  */
 static inline int
-ufshcd_is_valid_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
+ufshcd_get_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
 {
-	return ((be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24) ==
-		 UPIU_TRANSACTION_RESPONSE) ? 0 : DID_ERROR << 16;
+	return be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24;
 }
 
 /**
@@ -316,14 +311,71 @@ static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
 {
 	int len;
 	if (lrbp->sense_buffer) {
-		len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
+		len = be16_to_cpu(lrbp->ucd_rsp_ptr->sc.sense_data_len);
 		memcpy(lrbp->sense_buffer,
-			lrbp->ucd_rsp_ptr->sense_data,
+			lrbp->ucd_rsp_ptr->sc.sense_data,
 			min_t(int, len, SCSI_SENSE_BUFFERSIZE));
 	}
 }
 
 /**
+ * ufshcd_query_to_cpu() - formats the received buffer in to the native cpu
+ * endian
+ * @response: upiu query response to convert
+ */
+static inline void ufshcd_query_to_cpu(struct utp_upiu_query *response)
+{
+	response->length = be16_to_cpu(response->length);
+	response->value = be32_to_cpu(response->value);
+}
+
+/**
+ * ufshcd_query_to_be() - formats the buffer before sending in to big endian
+ * @response: upiu query request to convert
+ */
+static inline void ufshcd_query_to_be(struct utp_upiu_query *request)
+{
+	request->length = cpu_to_be16(request->length);
+	request->value = cpu_to_be32(request->value);
+}
+
+/**
+ * ufshcd_copy_query_response() - Copy Query Response and descriptor
+ * @lrb - pointer to local reference block
+ * @query_res - pointer to the query result
+ */
+static
+void ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+	struct ufs_query_res *query_res = hba->query.response;
+
+	/* Get the UPIU response */
+	if (query_res) {
+		query_res->response = ufshcd_get_rsp_upiu_result(
+			lrbp->ucd_rsp_ptr) >> UPIU_RSP_CODE_OFFSET;
+
+		memcpy(&query_res->upiu_res, &lrbp->ucd_rsp_ptr->qr,
+			QUERY_OSF_SIZE);
+		ufshcd_query_to_cpu(&query_res->upiu_res);
+	}
+
+	/* Get the descriptor */
+	if (hba->query.descriptor && lrbp->ucd_rsp_ptr->qr.opcode ==
+			UPIU_QUERY_OPCODE_READ_DESC) {
+		u8 *descp = (u8 *)&lrbp->ucd_rsp_ptr +
+				GENERAL_UPIU_REQUEST_SIZE;
+		u16 len;
+
+		/* data segment length */
+		len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) &
+						MASK_QUERY_DATA_SEG_LEN;
+
+		memcpy(hba->query.descriptor, descp,
+			min_t(u16, len, UPIU_HEADER_DATA_SEGMENT_MAX_SIZE));
+	}
+}
+
+/**
  * ufshcd_hba_capabilities - Read controller capabilities
  * @hba: per adapter instance
  */
@@ -423,76 +475,154 @@ static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
 }
 
 /**
- * ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
- * @lrb - pointer to local reference block
+ * ufshcd_prepare_req_desc - Fills the requests header
+ * descriptor according to request
+ * lrbp: pointer to local reference block
+ * upiu_flags: flags required in the header
  */
-static void ufshcd_compose_upiu(struct ufshcd_lrb *lrbp)
+static void ufshcd_prepare_req_desc(struct ufshcd_lrb *lrbp, u32 *upiu_flags)
 {
-	struct utp_transfer_req_desc *req_desc;
-	struct utp_upiu_cmd *ucd_cmd_ptr;
+	struct utp_transfer_req_desc *req_desc = lrbp->utr_descriptor_ptr;
+	enum dma_data_direction cmd_dir =
+		lrbp->cmd->sc_data_direction;
 	u32 data_direction;
-	u32 upiu_flags;
+	u32 dword_0;
+
+	if (cmd_dir == DMA_FROM_DEVICE) {
+		data_direction = UTP_DEVICE_TO_HOST;
+		*upiu_flags = UPIU_CMD_FLAGS_READ;
+	} else if (cmd_dir == DMA_TO_DEVICE) {
+		data_direction = UTP_HOST_TO_DEVICE;
+		*upiu_flags = UPIU_CMD_FLAGS_WRITE;
+	} else {
+		data_direction = UTP_NO_DATA_TRANSFER;
+		*upiu_flags = UPIU_CMD_FLAGS_NONE;
+	}
 
-	ucd_cmd_ptr = lrbp->ucd_cmd_ptr;
-	req_desc = lrbp->utr_descriptor_ptr;
+	dword_0 = data_direction | (lrbp->command_type
+				<< UPIU_COMMAND_TYPE_OFFSET);
 
-	switch (lrbp->command_type) {
-	case UTP_CMD_TYPE_SCSI:
-		if (lrbp->cmd->sc_data_direction == DMA_FROM_DEVICE) {
-			data_direction = UTP_DEVICE_TO_HOST;
-			upiu_flags = UPIU_CMD_FLAGS_READ;
-		} else if (lrbp->cmd->sc_data_direction == DMA_TO_DEVICE) {
-			data_direction = UTP_HOST_TO_DEVICE;
-			upiu_flags = UPIU_CMD_FLAGS_WRITE;
-		} else {
-			data_direction = UTP_NO_DATA_TRANSFER;
-			upiu_flags = UPIU_CMD_FLAGS_NONE;
-		}
+	/* Transfer request descriptor header fields */
+	req_desc->header.dword_0 = cpu_to_le32(dword_0);
 
-		/* Transfer request descriptor header fields */
-		req_desc->header.dword_0 =
-			cpu_to_le32(data_direction | UTP_SCSI_COMMAND);
+	/*
+	 * assigning invalid value for command status. Controller
+	 * updates OCS on command completion, with the command
+	 * status
+	 */
+	req_desc->header.dword_2 =
+		cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
+}
 
-		/*
-		 * assigning invalid value for command status. Controller
-		 * updates OCS on command completion, with the command
-		 * status
-		 */
-		req_desc->header.dword_2 =
-			cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
+static inline bool ufshcd_is_query_req(struct ufshcd_lrb *lrbp)
+{
+	return lrbp->cmd ? lrbp->cmd->cmnd[0] == UFS_QUERY_RESERVED_SCSI_CMD :
+		false;
+}
 
-		/* command descriptor fields */
-		ucd_cmd_ptr->header.dword_0 =
-			cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_COMMAND,
-						      upiu_flags,
-						      lrbp->lun,
-						      lrbp->task_tag));
-		ucd_cmd_ptr->header.dword_1 =
-			cpu_to_be32(
-				UPIU_HEADER_DWORD(UPIU_COMMAND_SET_TYPE_SCSI,
-						  0,
-						  0,
-						  0));
-
-		/* Total EHS length and Data segment length will be zero */
-		ucd_cmd_ptr->header.dword_2 = 0;
-
-		ucd_cmd_ptr->exp_data_transfer_len =
-			cpu_to_be32(lrbp->cmd->sdb.length);
-
-		memcpy(ucd_cmd_ptr->cdb,
-		       lrbp->cmd->cmnd,
-		       (min_t(unsigned short,
-			      lrbp->cmd->cmd_len,
-			      MAX_CDB_SIZE)));
-		break;
+/**
+ * ufshcd_prepare_utp_scsi_cmd_upiu() - fills the utp_transfer_req_desc,
+ * for scsi commands
+ * @lrbp - local reference block pointer
+ * @upiu_flags - flags
+ */
+static
+void ufshcd_prepare_utp_scsi_cmd_upiu(struct ufshcd_lrb *lrbp, u32 upiu_flags)
+{
+	struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
+
+	/* command descriptor fields */
+	ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
+				UPIU_TRANSACTION_COMMAND, upiu_flags,
+				lrbp->lun, lrbp->task_tag);
+	ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
+				UPIU_COMMAND_SET_TYPE_SCSI, 0, 0, 0);
+
+	/* Total EHS length and Data segment length will be zero */
+	ucd_req_ptr->header.dword_2 = 0;
+
+	ucd_req_ptr->sc.exp_data_transfer_len =
+		cpu_to_be32(lrbp->cmd->sdb.length);
+
+	memcpy(ucd_req_ptr->sc.cdb, lrbp->cmd->cmnd,
+		(min_t(unsigned short, lrbp->cmd->cmd_len, MAX_CDB_SIZE)));
+}
+
+/**
+ * ufshcd_prepare_utp_query_req_upiu() - fills the utp_transfer_req_desc,
+ * for query requsts
+ * @hba: UFS hba
+ * @lrbp: local reference block pointer
+ * @upiu_flags: flags
+ */
+static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba,
+					struct ufshcd_lrb *lrbp,
+					u32 upiu_flags)
+{
+	struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
+	u16 len = hba->query.request->upiu_req.length;
+	u8 *descp = (u8 *)lrbp->ucd_req_ptr + GENERAL_UPIU_REQUEST_SIZE;
+
+	/* Query request header */
+	ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
+			UPIU_TRANSACTION_QUERY_REQ, upiu_flags,
+			lrbp->lun, lrbp->task_tag);
+	ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
+			0, hba->query.request->query_func, 0, 0);
+
+	/* Data segment length */
+	ucd_req_ptr->header.dword_2 = UPIU_HEADER_DWORD(
+			0, 0, len >> 8, (u8)len);
+
+	/* Copy the Query Request buffer as is */
+	memcpy(&lrbp->ucd_req_ptr->qr, &hba->query.request->upiu_req,
+			QUERY_OSF_SIZE);
+	ufshcd_query_to_be(&lrbp->ucd_req_ptr->qr);
+
+	/* Copy the Descriptor */
+	if (hba->query.descriptor != NULL && len > 0 &&
+		(hba->query.request->upiu_req.opcode ==
+					UPIU_QUERY_OPCODE_WRITE_DESC)) {
+		memcpy(descp, hba->query.descriptor,
+			min_t(u16, len, UPIU_HEADER_DATA_SEGMENT_MAX_SIZE));
+	}
+
+}
+
+/**
+ * ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
+ * @hba - UFS hba
+ * @lrb - pointer to local reference block
+ */
+static int ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+	u32 upiu_flags;
+	int ret = 0;
+
+	switch (lrbp->command_type) {
+	case UTP_CMD_TYPE_SCSI:
 	case UTP_CMD_TYPE_DEV_MANAGE:
-		/* For query function implementation */
+		ufshcd_prepare_req_desc(lrbp, &upiu_flags);
+		if (lrbp->command_type == UTP_CMD_TYPE_SCSI)
+			ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, upiu_flags);
+		else
+			ufshcd_prepare_utp_query_req_upiu(hba, lrbp,
+								upiu_flags);
 		break;
 	case UTP_CMD_TYPE_UFS:
 		/* For UFS native command implementation */
+		dev_err(hba->dev, "%s: UFS native command are not supported\n",
+			__func__);
+		ret = -ENOTSUPP;
+		break;
+	default:
+		ret = -ENOTSUPP;
+		dev_err(hba->dev, "%s: unknown command type: 0x%x\n",
+				__func__, lrbp->command_type);
 		break;
 	} /* end of switch */
+
+	return ret;
 }
 
 /**
@@ -527,10 +657,13 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
 	lrbp->task_tag = tag;
 	lrbp->lun = cmd->device->lun;
 
-	lrbp->command_type = UTP_CMD_TYPE_SCSI;
+	if (ufshcd_is_query_req(lrbp))
+		lrbp->command_type = UTP_CMD_TYPE_DEV_MANAGE;
+	else
+		lrbp->command_type = UTP_CMD_TYPE_SCSI;
 
 	/* form UPIU before issuing the command */
-	ufshcd_compose_upiu(lrbp);
+	ufshcd_compose_upiu(hba, lrbp);
 	err = ufshcd_map_sg(lrbp);
 	if (err)
 		goto out;
@@ -677,8 +810,8 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba)
 				cpu_to_le16(ALIGNED_UPIU_SIZE);
 
 		hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
-		hba->lrb[i].ucd_cmd_ptr =
-			(struct utp_upiu_cmd *)(cmd_descp + i);
+		hba->lrb[i].ucd_req_ptr =
+			(struct utp_upiu_req *)(cmd_descp + i);
 		hba->lrb[i].ucd_rsp_ptr =
 			(struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
 		hba->lrb[i].ucd_prdt_ptr =
@@ -1101,7 +1234,9 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
  * @hba: per adapter instance
  * @lrb: pointer to local reference block of completed command
  *
- * Returns result of the command to notify SCSI midlayer
+ * Returns result of the command to notify SCSI midlayer. In
+ * case of query request specific result, returns DID_OK, and
+ * the error will be handled by the dispatcher.
  */
 static inline int
 ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
@@ -1115,27 +1250,46 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 
 	switch (ocs) {
 	case OCS_SUCCESS:
-
 		/* check if the returned transfer response is valid */
-		result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
-		if (result) {
+		result = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
+
+		switch (result) {
+		case UPIU_TRANSACTION_RESPONSE:
+			/*
+			 * get the response UPIU result to extract
+			 * the SCSI command status
+			 */
+			result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
+
+			/*
+			 * get the result based on SCSI status response
+			 * to notify the SCSI midlayer of the command status
+			 */
+			scsi_status = result & MASK_SCSI_STATUS;
+			result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
+			break;
+		case UPIU_TRANSACTION_QUERY_RSP:
+			/*
+			 *  Return result = ok, since SCSI layer wouldn't
+			 *  know how to handle errors from query requests.
+			 *  The result is saved with the response so that
+			 *  the ufs_core layer will handle it.
+			 */
+			result = DID_OK << 16;
+			ufshcd_copy_query_response(hba, lrbp);
+			break;
+		case UPIU_TRANSACTION_REJECT_UPIU:
+			/* TODO: handle Reject UPIU Response */
+			result = DID_ERROR << 16;
 			dev_err(hba->dev,
-				"Invalid response = %x\n", result);
+				"Reject UPIU not fully implemented\n");
 			break;
+		default:
+			result = DID_ERROR << 16;
+			dev_err(hba->dev,
+				"Unexpected request response code = %x\n",
+				result);
 		}
-
-		/*
-		 * get the response UPIU result to extract
-		 * the SCSI command status
-		 */
-		result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
-
-		/*
-		 * get the result based on SCSI status response
-		 * to notify the SCSI midlayer of the command status
-		 */
-		scsi_status = result & MASK_SCSI_STATUS;
-		result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
 		break;
 	case OCS_ABORTED:
 		result |= DID_ABORT << 16;
@@ -1364,10 +1518,10 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba,
 	task_req_upiup =
 		(struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
 	task_req_upiup->header.dword_0 =
-		cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
-					      lrbp->lun, lrbp->task_tag));
+		UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
+					      lrbp->lun, lrbp->task_tag);
 	task_req_upiup->header.dword_1 =
-	cpu_to_be32(UPIU_HEADER_DWORD(0, tm_function, 0, 0));
+		UPIU_HEADER_DWORD(0, tm_function, 0, 0);
 
 	task_req_upiup->input_param1 = lrbp->lun;
 	task_req_upiup->input_param1 =
@@ -1670,6 +1824,9 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
 	INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
 	INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
 
+	/* Initialize mutex for query requests */
+	mutex_init(&hba->query.lock_ufs_query);
+
 	/* IRQ registration */
 	err = request_irq(irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
 	if (err) {
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 6b99a42..336980b 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -60,6 +60,7 @@
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsi_dbg.h>
 #include <scsi/scsi_eh.h>
+#include <scsi/scsi_device.h>
 
 #include "ufs.h"
 #include "ufshci.h"
@@ -88,7 +89,7 @@ struct uic_command {
 /**
  * struct ufshcd_lrb - local reference block
  * @utr_descriptor_ptr: UTRD address of the command
- * @ucd_cmd_ptr: UCD address of the command
+ * @ucd_req_ptr: UCD address of the command
  * @ucd_rsp_ptr: Response UPIU address for this command
  * @ucd_prdt_ptr: PRDT address of the command
  * @cmd: pointer to SCSI command
@@ -101,7 +102,7 @@ struct uic_command {
  */
 struct ufshcd_lrb {
 	struct utp_transfer_req_desc *utr_descriptor_ptr;
-	struct utp_upiu_cmd *ucd_cmd_ptr;
+	struct utp_upiu_req *ucd_req_ptr;
 	struct utp_upiu_rsp *ucd_rsp_ptr;
 	struct ufshcd_sg_entry *ucd_prdt_ptr;
 
@@ -115,6 +116,19 @@ struct ufshcd_lrb {
 	unsigned int lun;
 };
 
+/**
+ * struct ufs_query - keeps the query request information
+ * @request: request upiu and function
+ * @descriptor: buffer for sending/receiving descriptor
+ * @response: response upiu and response
+ * @mutex: lock to allow one query at a time
+ */
+struct ufs_query {
+	struct ufs_query_req *request;
+	u8 *descriptor;
+	struct ufs_query_res *response;
+	struct mutex lock_ufs_query;
+};
 
 /**
  * struct ufs_hba - per adapter private structure
@@ -143,6 +157,7 @@ struct ufshcd_lrb {
  * @uic_workq: Work queue for UIC completion handling
  * @feh_workq: Work queue for fatal controller error handling
  * @errors: HBA errors
+ * @query: query request information
  */
 struct ufs_hba {
 	void __iomem *mmio_base;
@@ -184,6 +199,9 @@ struct ufs_hba {
 
 	/* HBA Errors */
 	u32 errors;
+
+	/* Query Request */
+	struct ufs_query query;
 };
 
 int ufshcd_init(struct device *, struct ufs_hba ** , void __iomem * ,
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
index 0c16484..4a86247 100644
--- a/drivers/scsi/ufs/ufshci.h
+++ b/drivers/scsi/ufs/ufshci.h
@@ -39,7 +39,7 @@
 enum {
 	TASK_REQ_UPIU_SIZE_DWORDS	= 8,
 	TASK_RSP_UPIU_SIZE_DWORDS	= 8,
-	ALIGNED_UPIU_SIZE		= 128,
+	ALIGNED_UPIU_SIZE		= 512,
 };
 
 /* UFSHCI Registers */
-- 
1.7.6

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 2/9] Documentation: devicetree: Add DT bindings for UFS host controller
  2013-05-14  7:08 [PATCH V2 0/9] ufs patch siries Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 1/9] scsi: ufs: add support for query requests Dolev Raviv
@ 2013-05-14  7:08 ` Dolev Raviv
  2013-05-14 18:08   ` Olof Johansson
  2013-05-14  7:08 ` [PATCH V2 3/9] scsi: ufs: Fix the response UPIU length setting Dolev Raviv
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 12+ messages in thread
From: Dolev Raviv @ 2013-05-14  7:08 UTC (permalink / raw)
  To: linux-scsi
  Cc: linux-arm-msm, Dolev Raviv, Sujit Reddy Thumma,
	moderated list:OPEN FIRMWARE AND..., open list:DOCUMENTATION,
	open list

Compatible list is used in commit 03b1781 but is not documented.
Add necessary device tree bindings to describe on-chip UFS host
controllers.

Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>

diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
new file mode 100644
index 0000000..20468b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -0,0 +1,16 @@
+* Universal Flash Storage (UFS) Host Controller
+
+UFSHC nodes are defined to describe on-chip UFS host controllers.
+Each UFS controller instance should have its own node.
+
+Required properties:
+- compatible        : compatible list, contains "jedec,ufs-1.1"
+- interrupts        : <interrupt mapping for UFS host controller IRQ>
+- reg               : <registers mapping>
+
+Example:
+	ufshc@0xfc598000 {
+		compatible = "jedec,ufs-1.1";
+		reg = <0xfc598000 0x800>;
+		interrupts = <0 28 0>;
+	};
-- 
1.7.6

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 3/9] scsi: ufs: Fix the response UPIU length setting
  2013-05-14  7:08 [PATCH V2 0/9] ufs patch siries Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 1/9] scsi: ufs: add support for query requests Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 2/9] Documentation: devicetree: Add DT bindings for UFS host controller Dolev Raviv
@ 2013-05-14  7:08 ` Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 4/9] scsi: ufs: wrap the i/o access operations Dolev Raviv
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Dolev Raviv @ 2013-05-14  7:08 UTC (permalink / raw)
  To: linux-scsi
  Cc: linux-arm-msm, Dolev Raviv, Maya Erez, Sujit Reddy Thumma,
	open list

The response UPIU length should be in DWORD and not in bytes.

Signed-off-by: Maya Erez <merez@codeaurora.org>
Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
Tested-by: Dolev Raviv <draviv@codeaurora.org>

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 4ddc8be..7ce40a5 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -807,7 +807,7 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba)
 		utrdlp[i].prd_table_offset =
 				cpu_to_le16((prdt_offset >> 2));
 		utrdlp[i].response_upiu_length =
-				cpu_to_le16(ALIGNED_UPIU_SIZE);
+				cpu_to_le16(ALIGNED_UPIU_SIZE >> 2);
 
 		hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
 		hba->lrb[i].ucd_req_ptr =
-- 
1.7.6

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 4/9] scsi: ufs: wrap the i/o access operations
  2013-05-14  7:08 [PATCH V2 0/9] ufs patch siries Dolev Raviv
                   ` (2 preceding siblings ...)
  2013-05-14  7:08 ` [PATCH V2 3/9] scsi: ufs: Fix the response UPIU length setting Dolev Raviv
@ 2013-05-14  7:08 ` Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 5/9] scsi: ufs: amend interrupt configuration Dolev Raviv
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Dolev Raviv @ 2013-05-14  7:08 UTC (permalink / raw)
  To: linux-scsi; +Cc: linux-arm-msm, Dolev Raviv, Seungwon Jeon, open list

Simplify operations with hiding mmio_base.

Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
Tested-by: Maya Erez <merez@codeaurora.org>
Acked-by: Santosh Y <santoshsy@gmail.com>

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 7ce40a5..3946b9d 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -71,7 +71,7 @@ enum {
  */
 static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba)
 {
-	return readl(hba->mmio_base + REG_UFS_VERSION);
+	return ufshcd_readl(hba, REG_UFS_VERSION);
 }
 
 /**
@@ -130,8 +130,7 @@ static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba)
  */
 static inline void ufshcd_utrl_clear(struct ufs_hba *hba, u32 pos)
 {
-	writel(~(1 << pos),
-		(hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_CLEAR));
+	ufshcd_writel(hba, ~(1 << pos), REG_UTP_TRANSFER_REQ_LIST_CLEAR);
 }
 
 /**
@@ -165,7 +164,7 @@ static inline int ufshcd_get_lists_status(u32 reg)
  */
 static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
 {
-	return readl(hba->mmio_base + REG_UIC_COMMAND_ARG_2) &
+	return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_2) &
 	       MASK_UIC_COMMAND_RESULT;
 }
 
@@ -238,18 +237,15 @@ ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
 {
 	switch (option) {
 	case INT_AGGR_RESET:
-		writel((INT_AGGR_ENABLE |
-			INT_AGGR_COUNTER_AND_TIMER_RESET),
-			(hba->mmio_base +
-			 REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
+		ufshcd_writel(hba, INT_AGGR_ENABLE |
+			      INT_AGGR_COUNTER_AND_TIMER_RESET,
+			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
 		break;
 	case INT_AGGR_CONFIG:
-		writel((INT_AGGR_ENABLE |
-			INT_AGGR_PARAM_WRITE |
-			INT_AGGR_COUNTER_THRESHOLD_VALUE |
-			INT_AGGR_TIMEOUT_VALUE),
-			(hba->mmio_base +
-			 REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
+		ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
+			      INT_AGGR_COUNTER_THRESHOLD_VALUE |
+			      INT_AGGR_TIMEOUT_VALUE,
+			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
 		break;
 	}
 }
@@ -262,12 +258,10 @@ ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
  */
 static void ufshcd_enable_run_stop_reg(struct ufs_hba *hba)
 {
-	writel(UTP_TASK_REQ_LIST_RUN_STOP_BIT,
-	       (hba->mmio_base +
-		REG_UTP_TASK_REQ_LIST_RUN_STOP));
-	writel(UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
-	       (hba->mmio_base +
-		REG_UTP_TRANSFER_REQ_LIST_RUN_STOP));
+	ufshcd_writel(hba, UTP_TASK_REQ_LIST_RUN_STOP_BIT,
+		      REG_UTP_TASK_REQ_LIST_RUN_STOP);
+	ufshcd_writel(hba, UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
+		      REG_UTP_TRANSFER_REQ_LIST_RUN_STOP);
 }
 
 /**
@@ -276,7 +270,7 @@ static void ufshcd_enable_run_stop_reg(struct ufs_hba *hba)
  */
 static inline void ufshcd_hba_start(struct ufs_hba *hba)
 {
-	writel(CONTROLLER_ENABLE , (hba->mmio_base + REG_CONTROLLER_ENABLE));
+	ufshcd_writel(hba, CONTROLLER_ENABLE, REG_CONTROLLER_ENABLE);
 }
 
 /**
@@ -287,7 +281,7 @@ static inline void ufshcd_hba_start(struct ufs_hba *hba)
  */
 static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
 {
-	return (readl(hba->mmio_base + REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
+	return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
 }
 
 /**
@@ -299,8 +293,7 @@ static inline
 void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
 {
 	__set_bit(task_tag, &hba->outstanding_reqs);
-	writel((1 << task_tag),
-	       (hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL));
+	ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
 }
 
 /**
@@ -381,8 +374,7 @@ void ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
  */
 static inline void ufshcd_hba_capabilities(struct ufs_hba *hba)
 {
-	hba->capabilities =
-		readl(hba->mmio_base + REG_CONTROLLER_CAPABILITIES);
+	hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES);
 
 	/* nutrs and nutmrs are 0 based values */
 	hba->nutrs = (hba->capabilities & MASK_TRANSFER_REQUESTS_SLOTS) + 1;
@@ -399,16 +391,13 @@ static inline void
 ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
 {
 	/* Write Args */
-	writel(uic_cmnd->argument1,
-	      (hba->mmio_base + REG_UIC_COMMAND_ARG_1));
-	writel(uic_cmnd->argument2,
-	      (hba->mmio_base + REG_UIC_COMMAND_ARG_2));
-	writel(uic_cmnd->argument3,
-	      (hba->mmio_base + REG_UIC_COMMAND_ARG_3));
+	ufshcd_writel(hba, uic_cmnd->argument1, REG_UIC_COMMAND_ARG_1);
+	ufshcd_writel(hba, uic_cmnd->argument2, REG_UIC_COMMAND_ARG_2);
+	ufshcd_writel(hba, uic_cmnd->argument3, REG_UIC_COMMAND_ARG_3);
 
 	/* Write UIC Cmd */
-	writel((uic_cmnd->command & COMMAND_OPCODE_MASK),
-	       (hba->mmio_base + REG_UIC_COMMAND));
+	ufshcd_writel(hba, uic_cmnd->command & COMMAND_OPCODE_MASK,
+		      REG_UIC_COMMAND);
 }
 
 /**
@@ -460,16 +449,15 @@ static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
 {
 	switch (option) {
 	case UFSHCD_INT_ENABLE:
-		writel(hba->int_enable_mask,
-		      (hba->mmio_base + REG_INTERRUPT_ENABLE));
+		ufshcd_writel(hba, hba->int_enable_mask, REG_INTERRUPT_ENABLE);
 		break;
 	case UFSHCD_INT_DISABLE:
 		if (hba->ufs_version == UFSHCI_VERSION_10)
-			writel(INTERRUPT_DISABLE_MASK_10,
-			      (hba->mmio_base + REG_INTERRUPT_ENABLE));
+			ufshcd_writel(hba, INTERRUPT_DISABLE_MASK_10,
+				      REG_INTERRUPT_ENABLE);
 		else
-			writel(INTERRUPT_DISABLE_MASK_11,
-			       (hba->mmio_base + REG_INTERRUPT_ENABLE));
+			ufshcd_writel(hba, INTERRUPT_DISABLE_MASK_11,
+				      REG_INTERRUPT_ENABLE);
 		break;
 	}
 }
@@ -836,7 +824,7 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
 	unsigned long flags;
 
 	/* check if controller is ready to accept UIC commands */
-	if (((readl(hba->mmio_base + REG_CONTROLLER_STATUS)) &
+	if ((ufshcd_readl(hba, REG_CONTROLLER_STATUS) &
 	    UIC_COMMAND_READY) == 0x0) {
 		dev_err(hba->dev,
 			"Controller not ready"
@@ -881,7 +869,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
 	u32 reg;
 
 	/* check if device present */
-	reg = readl((hba->mmio_base + REG_CONTROLLER_STATUS));
+	reg = ufshcd_readl(hba, REG_CONTROLLER_STATUS);
 	if (!ufshcd_is_device_present(reg)) {
 		dev_err(hba->dev, "cc: Device not present\n");
 		err = -ENXIO;
@@ -1003,14 +991,14 @@ static int ufshcd_initialize_hba(struct ufs_hba *hba)
 		return -EIO;
 
 	/* Configure UTRL and UTMRL base address registers */
-	writel(lower_32_bits(hba->utrdl_dma_addr),
-	       (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_BASE_L));
-	writel(upper_32_bits(hba->utrdl_dma_addr),
-	       (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_BASE_H));
-	writel(lower_32_bits(hba->utmrdl_dma_addr),
-	       (hba->mmio_base + REG_UTP_TASK_REQ_LIST_BASE_L));
-	writel(upper_32_bits(hba->utmrdl_dma_addr),
-	       (hba->mmio_base + REG_UTP_TASK_REQ_LIST_BASE_H));
+	ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
+		      REG_UTP_TRANSFER_REQ_LIST_BASE_L);
+	ufshcd_writel(hba, upper_32_bits(hba->utrdl_dma_addr),
+		      REG_UTP_TRANSFER_REQ_LIST_BASE_H);
+	ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr),
+		      REG_UTP_TASK_REQ_LIST_BASE_L);
+	ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr),
+		      REG_UTP_TASK_REQ_LIST_BASE_H);
 
 	/* Initialize unipro link startup procedure */
 	return ufshcd_dme_link_startup(hba);
@@ -1323,8 +1311,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
 	int index;
 
 	lrb = hba->lrb;
-	tr_doorbell =
-		readl(hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL);
+	tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
 	completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
 
 	for (index = 0; index < hba->nutrs; index++) {
@@ -1398,9 +1385,7 @@ static void ufshcd_err_handler(struct ufs_hba *hba)
 		goto fatal_eh;
 
 	if (hba->errors & UIC_ERROR) {
-
-		reg = readl(hba->mmio_base +
-			    REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
+		reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
 		if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
 			goto fatal_eh;
 	}
@@ -1418,7 +1403,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba)
 {
 	u32 tm_doorbell;
 
-	tm_doorbell = readl(hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL);
+	tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
 	hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks;
 	wake_up_interruptible(&hba->ufshcd_tm_wait_queue);
 }
@@ -1459,15 +1444,14 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba)
 	struct ufs_hba *hba = __hba;
 
 	spin_lock(hba->host->host_lock);
-	intr_status = readl(hba->mmio_base + REG_INTERRUPT_STATUS);
+	intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
 
 	if (intr_status) {
 		ufshcd_sl_intr(hba, intr_status);
 
 		/* If UFSHCI 1.0 then clear interrupt status register */
 		if (hba->ufs_version == UFSHCI_VERSION_10)
-			writel(intr_status,
-			       (hba->mmio_base + REG_INTERRUPT_STATUS));
+			ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
 		retval = IRQ_HANDLED;
 	}
 	spin_unlock(hba->host->host_lock);
@@ -1532,8 +1516,7 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba,
 
 	/* send command to the controller */
 	__set_bit(free_slot, &hba->outstanding_tasks);
-	writel((1 << free_slot),
-	       (hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL));
+	ufshcd_writel(hba, 1 << free_slot, REG_UTP_TASK_REQ_DOOR_BELL);
 
 	spin_unlock_irqrestore(host->host_lock, flags);
 
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 336980b..6429bed 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -204,6 +204,11 @@ struct ufs_hba {
 	struct ufs_query query;
 };
 
+#define ufshcd_writel(hba, val, reg)	\
+	writel((val), (hba)->mmio_base + (reg))
+#define ufshcd_readl(hba, reg)	\
+	readl((hba)->mmio_base + (reg))
+
 int ufshcd_init(struct device *, struct ufs_hba ** , void __iomem * ,
 			unsigned int);
 void ufshcd_remove(struct ufs_hba *);
@@ -214,7 +219,7 @@ void ufshcd_remove(struct ufs_hba *);
  */
 static inline void ufshcd_hba_stop(struct ufs_hba *hba)
 {
-	writel(CONTROLLER_DISABLE, (hba->mmio_base + REG_CONTROLLER_ENABLE));
+	ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
 }
 
 #endif /* End of Header */
-- 
1.7.6

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 5/9] scsi: ufs: amend interrupt configuration
  2013-05-14  7:08 [PATCH V2 0/9] ufs patch siries Dolev Raviv
                   ` (3 preceding siblings ...)
  2013-05-14  7:08 ` [PATCH V2 4/9] scsi: ufs: wrap the i/o access operations Dolev Raviv
@ 2013-05-14  7:08 ` Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 6/9] scsi: ufs: fix interrupt status clears Dolev Raviv
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Dolev Raviv @ 2013-05-14  7:08 UTC (permalink / raw)
  To: linux-scsi; +Cc: linux-arm-msm, Dolev Raviv, Seungwon Jeon, open list

It makes interrupt setting more flexible especially
for disabling. And wrong bit mask is fixed for ver 1.0.
[17:16] is added for mask.

Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
Tested-by: Maya Erez <merez@codeaurora.org>
Acked-by: Santosh Y <santoshsy@gmail.com>

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 3946b9d..c342a38 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -35,6 +35,10 @@
 
 #include "ufshcd.h"
 
+#define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
+				 UTP_TASK_REQ_COMPL |\
+				 UFSHCD_ERROR_MASK)
+
 enum {
 	UFSHCD_MAX_CHANNEL	= 0,
 	UFSHCD_MAX_ID		= 1,
@@ -64,6 +68,20 @@ enum {
 };
 
 /**
+ * ufshcd_get_intr_mask - Get the interrupt bit mask
+ * @hba - Pointer to adapter instance
+ *
+ * Returns interrupt bit mask per version
+ */
+static inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba)
+{
+	if (hba->ufs_version == UFSHCI_VERSION_10)
+		return INTERRUPT_MASK_ALL_VER_10;
+	else
+		return INTERRUPT_MASK_ALL_VER_11;
+}
+
+/**
  * ufshcd_get_ufs_version - Get the UFS version supported by the HBA
  * @hba - Pointer to adapter instance
  *
@@ -441,25 +459,45 @@ static int ufshcd_map_sg(struct ufshcd_lrb *lrbp)
 }
 
 /**
- * ufshcd_int_config - enable/disable interrupts
+ * ufshcd_enable_intr - enable interrupts
  * @hba: per adapter instance
- * @option: interrupt option
+ * @intrs: interrupt bits
  */
-static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
+static void ufshcd_enable_intr(struct ufs_hba *hba, u32 intrs)
 {
-	switch (option) {
-	case UFSHCD_INT_ENABLE:
-		ufshcd_writel(hba, hba->int_enable_mask, REG_INTERRUPT_ENABLE);
-		break;
-	case UFSHCD_INT_DISABLE:
-		if (hba->ufs_version == UFSHCI_VERSION_10)
-			ufshcd_writel(hba, INTERRUPT_DISABLE_MASK_10,
-				      REG_INTERRUPT_ENABLE);
-		else
-			ufshcd_writel(hba, INTERRUPT_DISABLE_MASK_11,
-				      REG_INTERRUPT_ENABLE);
-		break;
+	u32 set = ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
+
+	if (hba->ufs_version == UFSHCI_VERSION_10) {
+		u32 rw;
+		rw = set & INTERRUPT_MASK_RW_VER_10;
+		set = rw | ((set ^ intrs) & intrs);
+	} else {
+		set |= intrs;
+	}
+
+	ufshcd_writel(hba, set, REG_INTERRUPT_ENABLE);
+}
+
+/**
+ * ufshcd_disable_intr - disable interrupts
+ * @hba: per adapter instance
+ * @intrs: interrupt bits
+ */
+static void ufshcd_disable_intr(struct ufs_hba *hba, u32 intrs)
+{
+	u32 set = ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
+
+	if (hba->ufs_version == UFSHCI_VERSION_10) {
+		u32 rw;
+		rw = (set & INTERRUPT_MASK_RW_VER_10) &
+			~(intrs & INTERRUPT_MASK_RW_VER_10);
+		set = rw | ((set & intrs) & ~INTERRUPT_MASK_RW_VER_10);
+
+	} else {
+		set &= ~intrs;
 	}
+
+	ufshcd_writel(hba, set, REG_INTERRUPT_ENABLE);
 }
 
 /**
@@ -842,8 +880,7 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
 	uic_cmd->argument3 = 0;
 
 	/* enable UIC related interrupts */
-	hba->int_enable_mask |= UIC_COMMAND_COMPL;
-	ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
+	ufshcd_enable_intr(hba, UIC_COMMAND_COMPL);
 
 	/* sending UIC commands to controller */
 	ufshcd_send_uic_command(hba, uic_cmd);
@@ -890,13 +927,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
 	}
 
 	/* Enable required interrupts */
-	hba->int_enable_mask |= (UTP_TRANSFER_REQ_COMPL |
-				 UIC_ERROR |
-				 UTP_TASK_REQ_COMPL |
-				 DEVICE_FATAL_ERROR |
-				 CONTROLLER_FATAL_ERROR |
-				 SYSTEM_BUS_FATAL_ERROR);
-	ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
+	ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
 
 	/* Configure interrupt aggregation */
 	ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
@@ -1724,7 +1755,7 @@ static void ufshcd_hba_free(struct ufs_hba *hba)
 void ufshcd_remove(struct ufs_hba *hba)
 {
 	/* disable interrupts */
-	ufshcd_int_config(hba, UFSHCD_INT_DISABLE);
+	ufshcd_disable_intr(hba, hba->intr_mask);
 
 	ufshcd_hba_stop(hba);
 	ufshcd_hba_free(hba);
@@ -1782,6 +1813,9 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
 	/* Get UFS version supported by the controller */
 	hba->ufs_version = ufshcd_get_ufs_version(hba);
 
+	/* Get Interrupt bit mask per version */
+	hba->intr_mask = ufshcd_get_intr_mask(hba);
+
 	/* Allocate memory for host memory space */
 	err = ufshcd_memory_alloc(hba);
 	if (err) {
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 6429bed..d98e046 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -153,7 +153,7 @@ struct ufs_query {
  * @ufshcd_tm_wait_queue: wait queue for task management
  * @tm_condition: condition variable for task management
  * @ufshcd_state: UFSHCD states
- * @int_enable_mask: Interrupt Mask Bits
+ * @intr_mask: Interrupt Mask Bits
  * @uic_workq: Work queue for UIC completion handling
  * @feh_workq: Work queue for fatal controller error handling
  * @errors: HBA errors
@@ -191,7 +191,7 @@ struct ufs_hba {
 	unsigned long tm_condition;
 
 	u32 ufshcd_state;
-	u32 int_enable_mask;
+	u32 intr_mask;
 
 	/* Work Queues */
 	struct work_struct uic_workq;
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
index 4a86247..f1e1b74 100644
--- a/drivers/scsi/ufs/ufshci.h
+++ b/drivers/scsi/ufs/ufshci.h
@@ -232,10 +232,11 @@ enum {
 /* Interrupt disable masks */
 enum {
 	/* Interrupt disable mask for UFSHCI v1.0 */
-	INTERRUPT_DISABLE_MASK_10	= 0xFFFF,
+	INTERRUPT_MASK_ALL_VER_10	= 0x30FFF,
+	INTERRUPT_MASK_RW_VER_10	= 0x30000,
 
 	/* Interrupt disable mask for UFSHCI v1.1 */
-	INTERRUPT_DISABLE_MASK_11	= 0x0,
+	INTERRUPT_MASK_ALL_VER_11	= 0x31FFF,
 };
 
 /*
-- 
1.7.6

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 6/9] scsi: ufs: fix interrupt status clears
  2013-05-14  7:08 [PATCH V2 0/9] ufs patch siries Dolev Raviv
                   ` (4 preceding siblings ...)
  2013-05-14  7:08 ` [PATCH V2 5/9] scsi: ufs: amend interrupt configuration Dolev Raviv
@ 2013-05-14  7:08 ` Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 7/9] scsi: ufs: rework link start-up process Dolev Raviv
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Dolev Raviv @ 2013-05-14  7:08 UTC (permalink / raw)
  To: linux-scsi; +Cc: linux-arm-msm, Dolev Raviv, Seungwon Jeon, open list

There is no need to check the version to clear
the interrupt status. And the order is changed
prior to actual handling.

Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
Tested-by: Maya Erez <merez@codeaurora.org>
Acked-by: Santosh Y <santoshsy@gmail.com>

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c342a38..f4293d1 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1478,11 +1478,8 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba)
 	intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
 
 	if (intr_status) {
+		ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
 		ufshcd_sl_intr(hba, intr_status);
-
-		/* If UFSHCI 1.0 then clear interrupt status register */
-		if (hba->ufs_version == UFSHCI_VERSION_10)
-			ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
 		retval = IRQ_HANDLED;
 	}
 	spin_unlock(hba->host->host_lock);
-- 
1.7.6

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 7/9] scsi: ufs: rework link start-up process
  2013-05-14  7:08 [PATCH V2 0/9] ufs patch siries Dolev Raviv
                   ` (5 preceding siblings ...)
  2013-05-14  7:08 ` [PATCH V2 6/9] scsi: ufs: fix interrupt status clears Dolev Raviv
@ 2013-05-14  7:08 ` Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 8/9] scsi: ufs: Add support for sending NOP OUT UPIU Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 9/9] scsi: ufs: Set fDeviceInit flag to initiate device initialization Dolev Raviv
  8 siblings, 0 replies; 12+ messages in thread
From: Dolev Raviv @ 2013-05-14  7:08 UTC (permalink / raw)
  To: linux-scsi
  Cc: linux-arm-msm, Dolev Raviv, Seungwon Jeon, Sujit Reddy Thumma,
	open list

Link start-up requires long time with multiphase handshakes
between UFS host and device. This affects driver's probe time.
This patch let link start-up run asynchronously. Link start-up
will be executed at the end of prove separately.
Along with this change, the following is worked.

Defined completion time of uic command to avoid a permanent wait.
Added mutex to guarantee of uic command at a time.
Adapted some sequence of controller initialization after link statup
according to HCI standard.

Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
Tested-by: Maya Erez <merez@codeaurora.org>
Acked-by: Santosh Y <santoshsy@gmail.com>

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index f4293d1..333812f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -33,11 +33,15 @@
  * this program.
  */
 
+#include <linux/async.h>
+
 #include "ufshcd.h"
 
 #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
 				 UTP_TASK_REQ_COMPL |\
 				 UFSHCD_ERROR_MASK)
+/* UIC command timeout, unit: ms */
+#define UIC_CMD_TIMEOUT	500
 
 enum {
 	UFSHCD_MAX_CHANNEL	= 0,
@@ -401,24 +405,122 @@ static inline void ufshcd_hba_capabilities(struct ufs_hba *hba)
 }
 
 /**
- * ufshcd_send_uic_command - Send UIC commands to unipro layers
+ * ufshcd_ready_for_uic_cmd - Check if controller is ready
+ *                            to accept UIC commands
  * @hba: per adapter instance
- * @uic_command: UIC command
+ * Return true on success, else false
+ */
+static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
+{
+	if (ufshcd_readl(hba, REG_CONTROLLER_STATUS) & UIC_COMMAND_READY)
+		return true;
+	else
+		return false;
+}
+
+/**
+ * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
+ * @hba: per adapter instance
+ * @uic_cmd: UIC command
+ *
+ * Mutex must be held.
  */
 static inline void
-ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
+ufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
 {
+	WARN_ON(hba->active_uic_cmd);
+
+	hba->active_uic_cmd = uic_cmd;
+
 	/* Write Args */
-	ufshcd_writel(hba, uic_cmnd->argument1, REG_UIC_COMMAND_ARG_1);
-	ufshcd_writel(hba, uic_cmnd->argument2, REG_UIC_COMMAND_ARG_2);
-	ufshcd_writel(hba, uic_cmnd->argument3, REG_UIC_COMMAND_ARG_3);
+	ufshcd_writel(hba, uic_cmd->argument1, REG_UIC_COMMAND_ARG_1);
+	ufshcd_writel(hba, uic_cmd->argument2, REG_UIC_COMMAND_ARG_2);
+	ufshcd_writel(hba, uic_cmd->argument3, REG_UIC_COMMAND_ARG_3);
 
 	/* Write UIC Cmd */
-	ufshcd_writel(hba, uic_cmnd->command & COMMAND_OPCODE_MASK,
+	ufshcd_writel(hba, uic_cmd->command & COMMAND_OPCODE_MASK,
 		      REG_UIC_COMMAND);
 }
 
 /**
+ * ufshcd_wait_for_uic_cmd - Wait complectioin of UIC command
+ * @hba: per adapter instance
+ * @uic_command: UIC command
+ *
+ * Must be called with mutex held.
+ * Returns 0 only if success.
+ */
+static int
+ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
+{
+	int ret;
+	unsigned long flags;
+
+	if (wait_for_completion_timeout(&uic_cmd->done,
+					msecs_to_jiffies(UIC_CMD_TIMEOUT)))
+		ret = uic_cmd->argument2 & MASK_UIC_COMMAND_RESULT;
+	else
+		ret = -ETIMEDOUT;
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->active_uic_cmd = NULL;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+	return ret;
+}
+
+/**
+ * __ufshcd_send_uic_cmd - Send UIC commands and retrieve the result
+ * @hba: per adapter instance
+ * @uic_cmd: UIC command
+ *
+ * Identical to ufshcd_send_uic_cmd() expect mutex. Must be called
+ * with mutex held.
+ * Returns 0 only if success.
+ */
+static int
+__ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
+{
+	int ret;
+	unsigned long flags;
+
+	if (!ufshcd_ready_for_uic_cmd(hba)) {
+		dev_err(hba->dev,
+			"Controller not ready to accept UIC commands\n");
+		return -EIO;
+	}
+
+	init_completion(&uic_cmd->done);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	ufshcd_dispatch_uic_cmd(hba, uic_cmd);
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+	ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd);
+
+	return ret;
+}
+
+/**
+ * ufshcd_send_uic_cmd - Send UIC commands and retrieve the result
+ * @hba: per adapter instance
+ * @uic_cmd: UIC command
+ *
+ * Returns 0 only if success.
+ */
+static int
+ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
+{
+	int ret;
+
+	mutex_lock(&hba->uic_cmd_mutex);
+	ret = __ufshcd_send_uic_cmd(hba, uic_cmd);
+	mutex_unlock(&hba->uic_cmd_mutex);
+
+	return ret;
+}
+
+/**
  * ufshcd_map_sg - Map scatter-gather list to prdt
  * @lrbp - pointer to local reference block
  *
@@ -858,34 +960,16 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba)
  */
 static int ufshcd_dme_link_startup(struct ufs_hba *hba)
 {
-	struct uic_command *uic_cmd;
-	unsigned long flags;
-
-	/* check if controller is ready to accept UIC commands */
-	if ((ufshcd_readl(hba, REG_CONTROLLER_STATUS) &
-	    UIC_COMMAND_READY) == 0x0) {
-		dev_err(hba->dev,
-			"Controller not ready"
-			" to accept UIC commands\n");
-		return -EIO;
-	}
+	struct uic_command uic_cmd = {0};
+	int ret;
 
-	spin_lock_irqsave(hba->host->host_lock, flags);
+	uic_cmd.command = UIC_CMD_DME_LINK_STARTUP;
 
-	/* form UIC command */
-	uic_cmd = &hba->active_uic_cmd;
-	uic_cmd->command = UIC_CMD_DME_LINK_STARTUP;
-	uic_cmd->argument1 = 0;
-	uic_cmd->argument2 = 0;
-	uic_cmd->argument3 = 0;
-
-	/* enable UIC related interrupts */
-	ufshcd_enable_intr(hba, UIC_COMMAND_COMPL);
-
-	/* sending UIC commands to controller */
-	ufshcd_send_uic_command(hba, uic_cmd);
-	spin_unlock_irqrestore(hba->host->host_lock, flags);
-	return 0;
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret)
+		dev_err(hba->dev,
+			"dme-link-startup: error code %d\n", ret);
+	return ret;
 }
 
 /**
@@ -894,9 +978,10 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
  *
  * To bring UFS host controller to operational state,
  * 1. Check if device is present
- * 2. Configure run-stop-registers
- * 3. Enable required interrupts
- * 4. Configure interrupt aggregation
+ * 2. Enable required interrupts
+ * 3. Configure interrupt aggregation
+ * 4. Program UTRL and UTMRL base addres
+ * 5. Configure run-stop-registers
  *
  * Returns 0 on success, non-zero value on failure
  */
@@ -913,6 +998,22 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
 		goto out;
 	}
 
+	/* Enable required interrupts */
+	ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
+
+	/* Configure interrupt aggregation */
+	ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
+
+	/* Configure UTRL and UTMRL base address registers */
+	ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
+			REG_UTP_TRANSFER_REQ_LIST_BASE_L);
+	ufshcd_writel(hba, upper_32_bits(hba->utrdl_dma_addr),
+			REG_UTP_TRANSFER_REQ_LIST_BASE_H);
+	ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr),
+			REG_UTP_TASK_REQ_LIST_BASE_L);
+	ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr),
+			REG_UTP_TASK_REQ_LIST_BASE_H);
+
 	/*
 	 * UCRDY, UTMRLDY and UTRLRDY bits must be 1
 	 * DEI, HEI bits must be 0
@@ -926,17 +1027,11 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
 		goto out;
 	}
 
-	/* Enable required interrupts */
-	ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
-
-	/* Configure interrupt aggregation */
-	ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
-
 	if (hba->ufshcd_state == UFSHCD_STATE_RESET)
 		scsi_unblock_requests(hba->host);
 
 	hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
-	scsi_scan_host(hba->host);
+
 out:
 	return err;
 }
@@ -1005,34 +1100,28 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
 }
 
 /**
- * ufshcd_initialize_hba - start the initialization process
+ * ufshcd_link_startup - Initialize unipro link startup
  * @hba: per adapter instance
  *
- * 1. Enable the controller via ufshcd_hba_enable.
- * 2. Program the Transfer Request List Address with the starting address of
- * UTRDL.
- * 3. Program the Task Management Request List Address with starting address
- * of UTMRDL.
- *
- * Returns 0 on success, non-zero value on failure.
+ * Returns 0 for success, non-zero in case of failure
  */
-static int ufshcd_initialize_hba(struct ufs_hba *hba)
+static int ufshcd_link_startup(struct ufs_hba *hba)
 {
-	if (ufshcd_hba_enable(hba))
-		return -EIO;
+	int ret;
 
-	/* Configure UTRL and UTMRL base address registers */
-	ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
-		      REG_UTP_TRANSFER_REQ_LIST_BASE_L);
-	ufshcd_writel(hba, upper_32_bits(hba->utrdl_dma_addr),
-		      REG_UTP_TRANSFER_REQ_LIST_BASE_H);
-	ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr),
-		      REG_UTP_TASK_REQ_LIST_BASE_L);
-	ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr),
-		      REG_UTP_TASK_REQ_LIST_BASE_H);
+	/* enable UIC related interrupts */
+	ufshcd_enable_intr(hba, UIC_COMMAND_COMPL);
 
-	/* Initialize unipro link startup procedure */
-	return ufshcd_dme_link_startup(hba);
+	ret = ufshcd_dme_link_startup(hba);
+	if (ret)
+		goto out;
+
+	ret = ufshcd_make_hba_operational(hba);
+
+out:
+	if (ret)
+		dev_err(hba->dev, "link startup failed %d\n", ret);
+	return ret;
 }
 
 /**
@@ -1072,12 +1161,19 @@ static int ufshcd_do_reset(struct ufs_hba *hba)
 	hba->outstanding_reqs = 0;
 	hba->outstanding_tasks = 0;
 
-	/* start the initialization process */
-	if (ufshcd_initialize_hba(hba)) {
+	/* Host controller enable */
+	if (ufshcd_hba_enable(hba)) {
 		dev_err(hba->dev,
 			"Reset: Controller initialization failed\n");
 		return FAILED;
 	}
+
+	if (ufshcd_link_startup(hba)) {
+		dev_err(hba->dev,
+			"Reset: Link start-up failed\n");
+		return FAILED;
+	}
+
 	return SUCCESS;
 }
 
@@ -1330,6 +1426,19 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 }
 
 /**
+ * ufshcd_uic_cmd_compl - handle completion of uic command
+ * @hba: per adapter instance
+ */
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+{
+	if (hba->active_uic_cmd) {
+		hba->active_uic_cmd->argument2 |=
+			ufshcd_get_uic_cmd_result(hba);
+		complete(&hba->active_uic_cmd->done);
+	}
+}
+
+/**
  * ufshcd_transfer_req_compl - handle SCSI and query command completion
  * @hba: per adapter instance
  */
@@ -1369,28 +1478,6 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
 }
 
 /**
- * ufshcd_uic_cc_handler - handle UIC command completion
- * @work: pointer to a work queue structure
- *
- * Returns 0 on success, non-zero value on failure
- */
-static void ufshcd_uic_cc_handler (struct work_struct *work)
-{
-	struct ufs_hba *hba;
-
-	hba = container_of(work, struct ufs_hba, uic_workq);
-
-	if ((hba->active_uic_cmd.command == UIC_CMD_DME_LINK_STARTUP) &&
-	    !(ufshcd_get_uic_cmd_result(hba))) {
-
-		if (ufshcd_make_hba_operational(hba))
-			dev_err(hba->dev,
-				"cc: hba not operational state\n");
-		return;
-	}
-}
-
-/**
  * ufshcd_fatal_err_handler - handle fatal errors
  * @hba: per adapter instance
  */
@@ -1451,7 +1538,7 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
 		ufshcd_err_handler(hba);
 
 	if (intr_status & UIC_COMMAND_COMPL)
-		schedule_work(&hba->uic_workq);
+		ufshcd_uic_cmd_compl(hba);
 
 	if (intr_status & UTP_TASK_REQ_COMPL)
 		ufshcd_tmc_handler(hba);
@@ -1674,6 +1761,21 @@ out:
 	return err;
 }
 
+/**
+ * ufshcd_async_scan - asynchronous execution for link startup
+ * @data: data pointer to pass to this function
+ * @cookie: cookie data
+ */
+static void ufshcd_async_scan(void *data, async_cookie_t cookie)
+{
+	struct ufs_hba *hba = (struct ufs_hba *)data;
+	int ret;
+
+	ret = ufshcd_link_startup(hba);
+	if (!ret)
+		scsi_scan_host(hba->host);
+}
+
 static struct scsi_host_template ufshcd_driver_template = {
 	.module			= THIS_MODULE,
 	.name			= UFSHCD,
@@ -1835,12 +1937,14 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
 	init_waitqueue_head(&hba->ufshcd_tm_wait_queue);
 
 	/* Initialize work queues */
-	INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
 	INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
 
 	/* Initialize mutex for query requests */
 	mutex_init(&hba->query.lock_ufs_query);
 
+	/* Initialize UIC command mutex */
+	mutex_init(&hba->uic_cmd_mutex);
+
 	/* IRQ registration */
 	err = request_irq(irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
 	if (err) {
@@ -1861,14 +1965,17 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
 		goto out_free_irq;
 	}
 
-	/* Initialization routine */
-	err = ufshcd_initialize_hba(hba);
+	/* Host controller enable */
+	err = ufshcd_hba_enable(hba);
 	if (err) {
-		dev_err(hba->dev, "Initialization failed\n");
+		dev_err(hba->dev, "Host controller enable failed\n");
 		goto out_remove_scsi_host;
 	}
+
 	*hba_handle = hba;
 
+	async_schedule(ufshcd_async_scan, hba);
+
 	return 0;
 
 out_remove_scsi_host:
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index d98e046..974bd07 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -51,6 +51,7 @@
 #include <linux/bitops.h>
 #include <linux/pm_runtime.h>
 #include <linux/clk.h>
+#include <linux/completion.h>
 
 #include <asm/irq.h>
 #include <asm/byteorder.h>
@@ -76,6 +77,7 @@
  * @argument3: UIC command argument 3
  * @cmd_active: Indicate if UIC command is outstanding
  * @result: UIC command result
+ * @done: UIC command completion
  */
 struct uic_command {
 	u32 command;
@@ -84,6 +86,7 @@ struct uic_command {
 	u32 argument3;
 	int cmd_active;
 	int result;
+	struct completion done;
 };
 
 /**
@@ -150,11 +153,11 @@ struct ufs_query {
  * @ufs_version: UFS Version to which controller complies
  * @irq: Irq number of the controller
  * @active_uic_cmd: handle of active UIC command
+ * @uic_cmd_mutex: mutex for uic command
  * @ufshcd_tm_wait_queue: wait queue for task management
  * @tm_condition: condition variable for task management
  * @ufshcd_state: UFSHCD states
  * @intr_mask: Interrupt Mask Bits
- * @uic_workq: Work queue for UIC completion handling
  * @feh_workq: Work queue for fatal controller error handling
  * @errors: HBA errors
  * @query: query request information
@@ -186,7 +189,9 @@ struct ufs_hba {
 	u32 ufs_version;
 	unsigned int irq;
 
-	struct uic_command active_uic_cmd;
+	struct uic_command *active_uic_cmd;
+	struct mutex uic_cmd_mutex;
+
 	wait_queue_head_t ufshcd_tm_wait_queue;
 	unsigned long tm_condition;
 
@@ -194,7 +199,6 @@ struct ufs_hba {
 	u32 intr_mask;
 
 	/* Work Queues */
-	struct work_struct uic_workq;
 	struct work_struct feh_workq;
 
 	/* HBA Errors */
-- 
1.7.6

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 8/9] scsi: ufs: Add support for sending NOP OUT UPIU
  2013-05-14  7:08 [PATCH V2 0/9] ufs patch siries Dolev Raviv
                   ` (6 preceding siblings ...)
  2013-05-14  7:08 ` [PATCH V2 7/9] scsi: ufs: rework link start-up process Dolev Raviv
@ 2013-05-14  7:08 ` Dolev Raviv
  2013-05-14  7:08 ` [PATCH V2 9/9] scsi: ufs: Set fDeviceInit flag to initiate device initialization Dolev Raviv
  8 siblings, 0 replies; 12+ messages in thread
From: Dolev Raviv @ 2013-05-14  7:08 UTC (permalink / raw)
  To: linux-scsi; +Cc: linux-arm-msm, Dolev Raviv, Sujit Reddy Thumma, open list

As part of device initialization sequence, sending NOP OUT UPIU and
waiting for NOP IN UPIU response is mandatory. This confirms that the
device UFS Transport (UTP) layer is functional and the host can configure
the device with further commands. Add support for sending NOP OUT UPIU to
check the device connection path and test whether the UTP layer on the
device side is functional during initialization.

Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
Acked-by: Santosh Y <santoshsy@gmail.com>
Tested-by: Dolev Raviv <draviv@codeaurora.org>

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 333812f..2db550b 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -43,6 +43,13 @@
 /* UIC command timeout, unit: ms */
 #define UIC_CMD_TIMEOUT	500
 
+/* NOP OUT retries waiting for NOP IN response */
+#define NOP_OUT_RETRIES    10
+/* Timeout after 30 msecs if NOP OUT hangs without response */
+#define NOP_OUT_TIMEOUT    30 /* msecs */
+/* Reserved tag for internal commands */
+#define INTERNAL_CMD_TAG   0
+
 enum {
 	UFSHCD_MAX_CHANNEL	= 0,
 	UFSHCD_MAX_ID		= 1,
@@ -71,6 +78,39 @@ enum {
 	INT_AGGR_CONFIG,
 };
 
+/*
+ * ufshcd_wait_for_register - wait for register value to change
+ * @hba - per-adapter interface
+ * @reg - mmio register offset
+ * @mask - mask to apply to read register value
+ * @val - wait condition
+ * @interval_us - polling interval in microsecs
+ * @timeout_ms - timeout in millisecs
+ *
+ * Returns final register value after iteration
+ */
+static u32 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
+		u32 val, unsigned long interval_us, unsigned long timeout_ms)
+{
+	u32 tmp;
+	ktime_t start;
+	unsigned long diff;
+
+	tmp = ufshcd_readl(hba, reg);
+
+	start = ktime_get();
+	while ((tmp & mask) == val) {
+		/* wakeup within 50us of expiry */
+		usleep_range(interval_us, interval_us + 50);
+		tmp = ufshcd_readl(hba, reg);
+		diff = ktime_to_ms(ktime_sub(ktime_get(), start));
+		if (diff > timeout_ms)
+			break;
+	}
+
+	return tmp;
+}
+
 /**
  * ufshcd_get_intr_mask - Get the interrupt bit mask
  * @hba - Pointer to adapter instance
@@ -612,7 +652,7 @@ static void ufshcd_prepare_req_desc(struct ufshcd_lrb *lrbp, u32 *upiu_flags)
 {
 	struct utp_transfer_req_desc *req_desc = lrbp->utr_descriptor_ptr;
 	enum dma_data_direction cmd_dir =
-		lrbp->cmd->sc_data_direction;
+		lrbp->cmd ? lrbp->cmd->sc_data_direction : DMA_NONE;
 	u32 data_direction;
 	u32 dword_0;
 
@@ -629,6 +669,8 @@ static void ufshcd_prepare_req_desc(struct ufshcd_lrb *lrbp, u32 *upiu_flags)
 
 	dword_0 = data_direction | (lrbp->command_type
 				<< UPIU_COMMAND_TYPE_OFFSET);
+	if (lrbp->intr_cmd)
+		dword_0 |= UTP_REQ_DESC_INT_CMD;
 
 	/* Transfer request descriptor header fields */
 	req_desc->header.dword_0 = cpu_to_le32(dword_0);
@@ -717,6 +759,18 @@ static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba,
 
 }
 
+static inline void ufshcd_prepare_utp_nop_upiu(struct ufshcd_lrb *lrbp)
+{
+	struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
+
+	memset(ucd_req_ptr, 0, sizeof(struct utp_upiu_req));
+
+	/* command descriptor fields */
+	ucd_req_ptr->header.dword_0 =
+		UPIU_HEADER_DWORD(
+			UPIU_TRANSACTION_NOP_OUT, 0, 0, lrbp->task_tag);
+}
+
 /**
  * ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
  * @hba - UFS hba
@@ -731,11 +785,13 @@ static int ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 	case UTP_CMD_TYPE_SCSI:
 	case UTP_CMD_TYPE_DEV_MANAGE:
 		ufshcd_prepare_req_desc(lrbp, &upiu_flags);
-		if (lrbp->command_type == UTP_CMD_TYPE_SCSI)
+		if (lrbp->cmd && lrbp->command_type == UTP_CMD_TYPE_SCSI)
 			ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, upiu_flags);
-		else
+		else if (lrbp->cmd && ufshcd_is_query_req(lrbp))
 			ufshcd_prepare_utp_query_req_upiu(hba, lrbp,
 								upiu_flags);
+		else if (!lrbp->cmd)
+			ufshcd_prepare_utp_nop_upiu(lrbp);
 		break;
 	case UTP_CMD_TYPE_UFS:
 		/* For UFS native command implementation */
@@ -784,6 +840,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
 	lrbp->sense_buffer = cmd->sense_buffer;
 	lrbp->task_tag = tag;
 	lrbp->lun = cmd->device->lun;
+	lrbp->intr_cmd = false;
 
 	if (ufshcd_is_query_req(lrbp))
 		lrbp->command_type = UTP_CMD_TYPE_DEV_MANAGE;
@@ -972,6 +1029,104 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
 	return ret;
 }
 
+static int
+ufshcd_compose_nop_out_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+	lrbp->cmd = NULL;
+	lrbp->sense_bufflen = 0;
+	lrbp->sense_buffer = NULL;
+	lrbp->task_tag = INTERNAL_CMD_TAG;
+	lrbp->lun = 0; /* NOP OUT is not specific to any LUN */
+	lrbp->command_type = UTP_CMD_TYPE_DEV_MANAGE;
+	lrbp->intr_cmd = true; /* No interrupt aggregation */
+
+	return ufshcd_compose_upiu(hba, lrbp);
+}
+
+static int ufshcd_wait_for_nop_cmd(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+	int err = 0;
+	unsigned long timeout;
+	unsigned long flags;
+
+	timeout = wait_for_completion_timeout(
+			lrbp->completion, msecs_to_jiffies(NOP_OUT_TIMEOUT));
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	lrbp->completion = NULL;
+	if (timeout)
+		err = ufshcd_get_tr_ocs(lrbp);
+	else
+		err = -ETIMEDOUT;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+	return err;
+}
+
+/**
+ * ufshcd_validate_dev_connection() - Check device connection status
+ * @hba: per-adapter instance
+ *
+ * Send NOP OUT UPIU and wait for NOP IN response to check whether the
+ * device Transport Protocol (UTP) layer is ready after a reset.
+ * If the UTP layer at the device side is not initialized, it may
+ * not respond with NOP IN UPIU within timeout of %NOP_OUT_TIMEOUT
+ * and we retry sending NOP OUT for %NOP_OUT_RETRIES iterations.
+ */
+static int ufshcd_validate_dev_connection(struct ufs_hba *hba)
+{
+	int err;
+	struct ufshcd_lrb *lrbp;
+	unsigned long flags;
+	struct completion wait;
+	int retries = NOP_OUT_RETRIES;
+
+retry:
+	init_completion(&wait);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	lrbp = &hba->lrb[INTERNAL_CMD_TAG];
+	err = ufshcd_compose_nop_out_upiu(hba, lrbp);
+	if (unlikely(err)) {
+		spin_unlock_irqrestore(hba->host->host_lock, flags);
+		goto out;
+	}
+
+	lrbp->completion = &wait;
+	ufshcd_send_command(hba, INTERNAL_CMD_TAG);
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+	err = ufshcd_wait_for_nop_cmd(hba, lrbp);
+
+	if (err == -ETIMEDOUT) {
+		u32 reg;
+		u32 mask = 1 << INTERNAL_CMD_TAG;
+
+		/* clear outstanding transaction before retry */
+		spin_lock_irqsave(hba->host->host_lock, flags);
+		ufshcd_utrl_clear(hba, INTERNAL_CMD_TAG);
+		__clear_bit(INTERNAL_CMD_TAG, &hba->outstanding_reqs);
+		spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+		/* poll for max. 1 sec to clear door bell register by h/w */
+		reg = ufshcd_wait_for_register(hba,
+				REG_UTP_TRANSFER_REQ_DOOR_BELL,
+				mask, mask, 1000, 1000);
+		if ((reg & mask) == mask)
+			retries = 0;
+	}
+
+	if (err && retries--) {
+		dev_dbg(hba->dev, "%s: error %d retrying\n", __func__, err);
+		goto retry;
+	}
+
+out:
+	if (err)
+		dev_err(hba->dev, "%s: NOP OUT failed %d\n", __func__, err);
+	return err;
+}
+
 /**
  * ufshcd_make_hba_operational - Make UFS controller operational
  * @hba: per adapter instance
@@ -1438,6 +1593,16 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
 	}
 }
 
+/*
+ * ufshcd_is_nop_out_upiu() - check if the command is NOP OUT UPIU
+ * @lrbp: pointer to logical reference block
+ */
+static inline bool ufshcd_is_nop_out_upiu(struct ufshcd_lrb *lrbp)
+{
+	return (be32_to_cpu(lrbp->ucd_req_ptr->header.dword_0) >> 24) ==
+				UPIU_TRANSACTION_NOP_OUT;
+}
+
 /**
  * ufshcd_transfer_req_compl - handle SCSI and query command completion
  * @hba: per adapter instance
@@ -1449,6 +1614,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
 	u32 tr_doorbell;
 	int result;
 	int index;
+	bool int_aggr_reset = true;
 
 	lrb = hba->lrb;
 	tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
@@ -1456,17 +1622,21 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
 
 	for (index = 0; index < hba->nutrs; index++) {
 		if (test_bit(index, &completed_reqs)) {
-
-			result = ufshcd_transfer_rsp_status(hba, &lrb[index]);
-
 			if (lrb[index].cmd) {
+				result = ufshcd_transfer_rsp_status(
+						hba, &lrb[index]);
 				scsi_dma_unmap(lrb[index].cmd);
 				lrb[index].cmd->result = result;
 				lrb[index].cmd->scsi_done(lrb[index].cmd);
 
 				/* Mark completed command as NULL in LRB */
 				lrb[index].cmd = NULL;
+			} else if (ufshcd_is_nop_out_upiu(&lrb[index])) {
+				if (lrb[index].completion)
+					complete(lrb[index].completion);
 			}
+			/* Don't reset counters for interrupt cmd */
+			int_aggr_reset = lrb[index].intr_cmd ? false : true;
 		} /* end of if */
 	} /* end of for */
 
@@ -1474,7 +1644,8 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
 	hba->outstanding_reqs ^= completed_reqs;
 
 	/* Reset interrupt aggregation counters */
-	ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
+	if (int_aggr_reset)
+		ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
 }
 
 /**
@@ -1772,8 +1943,16 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
 	int ret;
 
 	ret = ufshcd_link_startup(hba);
-	if (!ret)
-		scsi_scan_host(hba->host);
+	if (ret)
+		goto out;
+
+	ret = ufshcd_validate_dev_connection(hba);
+	if (ret)
+		goto out;
+
+	scsi_scan_host(hba->host);
+out:
+	return;
 }
 
 static struct scsi_host_template ufshcd_driver_template = {
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 974bd07..558d2f4 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -102,6 +102,8 @@ struct uic_command {
  * @command_type: SCSI, UFS, Query.
  * @task_tag: Task tag of the command
  * @lun: LUN of the command
+ * @intr_cmd: Interrupt command (doesn't participate in interrupt aggregation)
+ * @completion: holds the state of completion (used for internal commands)
  */
 struct ufshcd_lrb {
 	struct utp_transfer_req_desc *utr_descriptor_ptr;
@@ -117,6 +119,8 @@ struct ufshcd_lrb {
 	int command_type;
 	int task_tag;
 	unsigned int lun;
+	bool intr_cmd;
+	struct completion *completion;
 };
 
 /**
-- 
1.7.6

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 9/9] scsi: ufs: Set fDeviceInit flag to initiate device initialization
  2013-05-14  7:08 [PATCH V2 0/9] ufs patch siries Dolev Raviv
                   ` (7 preceding siblings ...)
  2013-05-14  7:08 ` [PATCH V2 8/9] scsi: ufs: Add support for sending NOP OUT UPIU Dolev Raviv
@ 2013-05-14  7:08 ` Dolev Raviv
  8 siblings, 0 replies; 12+ messages in thread
From: Dolev Raviv @ 2013-05-14  7:08 UTC (permalink / raw)
  To: linux-scsi; +Cc: linux-arm-msm, Dolev Raviv, Sujit Reddy Thumma, open list

Allow UFS device to complete its initialization and accept
SCSI commands by setting fDeviceInit flag. The device may take
time for this operation and hence the host should poll until
fDeviceInit flag is toggled to zero. This step is mandated by
UFS device specification for device initialization completion.

Signed-off-by: Dolev Raviv <draviv@codeaurora.org>
Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
Acked-by: Santosh Y <santoshsy@gmail.com>

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 086ff03..742363d 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -107,8 +107,13 @@ enum {
 	UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81,
 };
 
+/* Flag idn for Query Requests*/
+enum flag_idn {
+	QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
+};
+
 /* UTP QUERY Transaction Specific Fields OpCode */
-enum {
+enum query_opcode {
 	UPIU_QUERY_OPCODE_NOP		= 0x0,
 	UPIU_QUERY_OPCODE_READ_DESC	= 0x1,
 	UPIU_QUERY_OPCODE_WRITE_DESC	= 0x2,
@@ -208,6 +213,9 @@ struct utp_upiu_query {
 	u32 reserved[2];
 };
 
+/* Expose the flag value from utp_upiu_query.value */
+#define MASK_QUERY_UPIU_FLAG_LOC 0xFF
+
 /**
  * struct utp_upiu_req - general upiu request structure
  * @header:UPIU header structure DW-0 to DW-2
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2db550b..e9dba33 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -50,6 +50,15 @@
 /* Reserved tag for internal commands */
 #define INTERNAL_CMD_TAG   0
 
+/* Query request standart retries */
+#define QUERY_REQ_RETRIES 10
+/* Query request standart timeout  in seconds */
+#define QUERY_REQ_TIMEOUT 5
+/* Send Query Requst with default parameters */
+#define send_query_request(ARG1, ARG2, ARG3, ARG4)	\
+	ufshcd_query_request(ARG1, ARG2, ARG3, ARG4,	\
+			QUERY_REQ_TIMEOUT, QUERY_REQ_RETRIES)
+
 enum {
 	UFSHCD_MAX_CHANNEL	= 0,
 	UFSHCD_MAX_ID		= 1,
@@ -862,6 +871,164 @@ out:
 }
 
 /**
+ *  ufshcd_query_request() - Entry point for issuing query request to a
+ *  ufs device.
+ *  @hba: ufs driver context
+ *  @query: params for query request
+ *  @descriptor: buffer for sending/receiving descriptor
+ *  @response: pointer to a buffer that will contain the response code and
+ *           response upiu
+ *  @timeout: time limit for the command in seconds
+ *  @retries: number of times to try executing the command
+ *
+ *  The query request is submitted to the same request queue as the rest of
+ *  the scsi commands passed to the UFS controller. In order to use this
+ *  queue, we need to receive a tag, same as all other commands. The tags
+ *  are issued from the block layer. To simulate a request from the block
+ *  layer, we use the same interface as the SCSI layer does when it issues
+ *  commands not generated by users. To distinguish a query request from
+ *  the SCSI commands, we use a vendor specific unused SCSI command
+ *  op-code. This op-code is not part of the SCSI command subset used in
+ *  UFS. In such way it is easy to check the command in the driver and
+ *  handle it appropriately.
+ *
+ *  All necessary fields for issuing a query and receiving its response are
+ *  stored in the UFS hba struct. We can use this method since we know
+ *  there is only one active query request at all times.
+ *
+ *  The request that will pass to the device is stored in "query" argument
+ *  passed to this function, while the "response" argument (which is output
+ *  field) will hold the query response from the device along with the
+ *  response code.
+ */
+int ufshcd_query_request(struct ufs_hba *hba,
+			struct ufs_query_req *query,
+			u8 *descriptor,
+			struct ufs_query_res *response,
+			int timeout,
+			int retries)
+{
+	struct scsi_device *sdev;
+	u8 cmd[UFS_QUERY_CMD_SIZE] = {0};
+	int result;
+	bool sdev_lookup = true;
+
+	BUG_ON(!hba);
+	if (!query || !response) {
+		dev_err(hba->dev,
+		"%s: NULL pointer hba = %p, query = %p response = %p\n",
+		__func__, hba, query, response);
+		return -EINVAL;
+	}
+
+	/*
+	 * A SCSI command structure is composed from opcode at the
+	 * begining and 0 at the end.
+	 */
+	cmd[0] = UFS_QUERY_RESERVED_SCSI_CMD;
+
+	/* extracting the SCSI Device */
+	sdev = scsi_device_lookup(hba->host, 0, 0, 0);
+	if (!sdev) {
+		/**
+		 * There are some Query Requests that are sent during device
+		 * initialization, this happens before the scsi device was
+		 * initialized. If there is no scsi device, we generate a
+		 * temporary device to allow the Query Request flow.
+		 */
+		sdev_lookup = false;
+		sdev = scsi_get_host_dev(hba->host);
+	}
+
+	if (!sdev) {
+		dev_err(hba->dev, "%s: Could not fetch scsi device\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	mutex_lock(&hba->query.lock_ufs_query);
+	hba->query.request = query;
+	hba->query.descriptor = descriptor;
+	hba->query.response = response;
+
+	/* wait until request is completed */
+	result = scsi_execute(sdev, cmd, DMA_NONE, NULL, 0, NULL,
+				timeout, retries, 0, NULL);
+	if (result) {
+		dev_err(hba->dev,
+			"%s: Query with opcode 0x%x, failed with result %d\n",
+			__func__, query->upiu_req.opcode, result);
+		result = -EIO;
+	}
+
+	hba->query.request = NULL;
+	hba->query.descriptor = NULL;
+	hba->query.response = NULL;
+	mutex_unlock(&hba->query.lock_ufs_query);
+
+	/* Releasing scsi device resource */
+	if (sdev_lookup)
+		scsi_device_put(sdev);
+	else
+		scsi_free_host_dev(sdev);
+
+	return result;
+}
+
+/**
+ * ufshcd_query_flag() - Helper function for composing flag query requests
+ * hba: per-adapter instance
+ * query_opcode: flag query to perform
+ * idn: flag idn to access
+ * flag_res: the flag value after the query request completes
+ *
+ * Returns 0 for success, non-zero in case of failure
+ */
+int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
+			enum flag_idn idn, bool *flag_res)
+{
+	struct ufs_query_req query = {0};
+	struct ufs_query_res response = {0};
+	int err;
+
+	switch (opcode) {
+	case UPIU_QUERY_OPCODE_SET_FLAG:
+	case UPIU_QUERY_OPCODE_CLEAR_FLAG:
+	case UPIU_QUERY_OPCODE_TOGGLE_FLAG:
+		query.query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST;
+		break;
+	case UPIU_QUERY_OPCODE_READ_FLAG:
+		query.query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST;
+		break;
+	default:
+		dev_err(hba->dev,
+			"%s: Expected query flag opcode but got = %d\n",
+			__func__, opcode);
+		err = -EINVAL;
+		goto out;
+	}
+	query.upiu_req.opcode = opcode;
+	query.upiu_req.idn = idn;
+
+	/* Send query request */
+	err = send_query_request(hba, &query, NULL, &response);
+
+	if (err) {
+		dev_err(hba->dev,
+			"%s: Sending flag query for idn %d failed, err = %d\n",
+			__func__, idn, err);
+		goto out;
+	}
+
+	if (flag_res)
+		*flag_res = (response.upiu_res.value &
+				MASK_QUERY_UPIU_FLAG_LOC) & 0x1;
+
+out:
+	return err;
+}
+
+/**
  * ufshcd_memory_alloc - allocate memory for host memory space data structures
  * @hba: per adapter instance
  *
@@ -1128,6 +1295,44 @@ out:
 }
 
 /**
+ * ufshcd_validate_device_init() - checks device readines
+ * hba: per-adapter instance
+ *
+ * Set fDeviceInit flag, than, query the flag untill the device clears the
+ * flag.
+ */
+static int ufshcd_validate_device_init(struct ufs_hba *hba)
+{
+	int i, err;
+	bool flag_res = 0;
+
+	err = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_SET_FLAG,
+				QUERY_FLAG_IDN_FDEVICEINIT, &flag_res);
+	if (err) {
+		dev_err(hba->dev,
+			"%s setting fDeviceInit flag failed with error %d\n",
+			__func__, err);
+		goto out;
+	}
+
+	for (i = 0; i < QUERY_REQ_RETRIES && !err && flag_res; i++) {
+		err = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG,
+				QUERY_FLAG_IDN_FDEVICEINIT, &flag_res);
+	}
+	if (err)
+		dev_err(hba->dev,
+			"%s reading fDeviceInit flag failed with error %d\n",
+			__func__, err);
+	else if (flag_res)
+		dev_err(hba->dev,
+			"%s fDeviceInit was not cleared by the device\n",
+			__func__);
+
+out:
+	return err;
+}
+
+/**
  * ufshcd_make_hba_operational - Make UFS controller operational
  * @hba: per adapter instance
  *
@@ -1950,6 +2155,10 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
 	if (ret)
 		goto out;
 
+	ret = ufshcd_validate_device_init(hba);
+	if (ret)
+		goto out;
+
 	scsi_scan_host(hba->host);
 out:
 	return;
-- 
1.7.6

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH V2 2/9] Documentation: devicetree: Add DT bindings for UFS host controller
  2013-05-14  7:08 ` [PATCH V2 2/9] Documentation: devicetree: Add DT bindings for UFS host controller Dolev Raviv
@ 2013-05-14 18:08   ` Olof Johansson
       [not found]     ` <51933768.20107@codeaurora.org>
  0 siblings, 1 reply; 12+ messages in thread
From: Olof Johansson @ 2013-05-14 18:08 UTC (permalink / raw)
  To: Dolev Raviv
  Cc: linux-scsi, linux-arm-msm, Sujit Reddy Thumma,
	moderated list:OPEN FIRMWARE AND..., open list:DOCUMENTATION,
	open list

On Tue, May 14, 2013 at 12:08 AM, Dolev Raviv <draviv@codeaurora.org> wrote:
> Compatible list is used in commit 03b1781 but is not documented.
> Add necessary device tree bindings to describe on-chip UFS host
> controllers.
>
> Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
>
> diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
> new file mode 100644
> index 0000000..20468b2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
> @@ -0,0 +1,16 @@
> +* Universal Flash Storage (UFS) Host Controller
> +
> +UFSHC nodes are defined to describe on-chip UFS host controllers.
> +Each UFS controller instance should have its own node.
> +
> +Required properties:
> +- compatible        : compatible list, contains "jedec,ufs-1.1"
> +- interrupts        : <interrupt mapping for UFS host controller IRQ>
> +- reg               : <registers mapping>
> +
> +Example:
> +       ufshc@0xfc598000 {
> +               compatible = "jedec,ufs-1.1";

Hmm.

Does jedec really specify the programming interface of this type of
device, register layout and meaning? It seems to be more about the
command set and electrical/connectivity specifications, no?


-Olof

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V2 2/9] Documentation: devicetree: Add DT bindings for UFS host controller
       [not found]       ` <51933768.20107-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2013-05-28  5:31         ` Olof Johansson
  0 siblings, 0 replies; 12+ messages in thread
From: Olof Johansson @ 2013-05-28  5:31 UTC (permalink / raw)
  To: Sujit Reddy Thumma
  Cc: linux-scsi-u79uwXL29TY76Z2rM5mHXA, DOCUMENTATION,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	moderated list:OPEN FIRMWARE AND..., open list, Dolev Raviv

On Wed, May 15, 2013 at 12:51:12PM +0530, Sujit Reddy Thumma wrote:
> On 5/14/2013 11:38 PM, Olof Johansson wrote:
> >On Tue, May 14, 2013 at 12:08 AM, Dolev Raviv <draviv-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org> wrote:
> >>Compatible list is used in commit 03b1781 but is not documented.
> >>Add necessary device tree bindings to describe on-chip UFS host
> >>controllers.
> >>
> >>Signed-off-by: Sujit Reddy Thumma <sthumma-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> >>
> >>diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
> >>new file mode 100644
> >>index 0000000..20468b2
> >>--- /dev/null
> >>+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
> >>@@ -0,0 +1,16 @@
> >>+* Universal Flash Storage (UFS) Host Controller
> >>+
> >>+UFSHC nodes are defined to describe on-chip UFS host controllers.
> >>+Each UFS controller instance should have its own node.
> >>+
> >>+Required properties:
> >>+- compatible        : compatible list, contains "jedec,ufs-1.1"
> >>+- interrupts        : <interrupt mapping for UFS host controller IRQ>
> >>+- reg               : <registers mapping>
> >>+
> >>+Example:
> >>+       ufshc@0xfc598000 {
> >>+               compatible = "jedec,ufs-1.1";
> >
> >Hmm.
> >
> >Does jedec really specify the programming interface of this type of
> >device, register layout and meaning? It seems to be more about the
> >command set and electrical/connectivity specifications, no?
> 
> That's true with UFS device specification. For UFS Host Controller
> specification Jedec indeed standardized the hardware registers,
> memory structures and register programming sequence to talk to UFS
> device.

Ok, fair enough. However, I think "jedec,ufshc-1.1" would be more
appropriate to avoid ambiguities. Compare to SD where "sdhci" is the
only compatible string used for the standard interface.


-Olof

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2013-05-28  5:31 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-05-14  7:08 [PATCH V2 0/9] ufs patch siries Dolev Raviv
2013-05-14  7:08 ` [PATCH V2 1/9] scsi: ufs: add support for query requests Dolev Raviv
2013-05-14  7:08 ` [PATCH V2 2/9] Documentation: devicetree: Add DT bindings for UFS host controller Dolev Raviv
2013-05-14 18:08   ` Olof Johansson
     [not found]     ` <51933768.20107@codeaurora.org>
     [not found]       ` <51933768.20107-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2013-05-28  5:31         ` Olof Johansson
2013-05-14  7:08 ` [PATCH V2 3/9] scsi: ufs: Fix the response UPIU length setting Dolev Raviv
2013-05-14  7:08 ` [PATCH V2 4/9] scsi: ufs: wrap the i/o access operations Dolev Raviv
2013-05-14  7:08 ` [PATCH V2 5/9] scsi: ufs: amend interrupt configuration Dolev Raviv
2013-05-14  7:08 ` [PATCH V2 6/9] scsi: ufs: fix interrupt status clears Dolev Raviv
2013-05-14  7:08 ` [PATCH V2 7/9] scsi: ufs: rework link start-up process Dolev Raviv
2013-05-14  7:08 ` [PATCH V2 8/9] scsi: ufs: Add support for sending NOP OUT UPIU Dolev Raviv
2013-05-14  7:08 ` [PATCH V2 9/9] scsi: ufs: Set fDeviceInit flag to initiate device initialization Dolev Raviv

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).