[PATCH V3 0/2] Add suport for internal request (NOP and Query Request)

2013-07-09 Thread Sujit Reddy Thumma
This patch series replace the previous Query Request and NOP patches:
[PATCH 1/8] scsi: ufs: add support for query
[PATCH 6/8] scsi: ufs: Add support for sending NOP OUT UPIU
[PATCH 7/8] scsi: ufs: Set fDeviceInit flag to initiate device initialization
Major difference -

Sending the query request via the SCSI vendor specific command can
cause a deadlock in case the SCSI command queue is blocked and we
would like to send a query request (for example fDeviceInit in case
of re-initialization). In addition, usage of a vendor specific SCSI
command for UFS can cause future conflicts if this vendor specific
command will be allocated for a different usage.

The new patch series gets an internal tag for NOP and query requests
and do not involve the SCSI layer in UFS specific requests transfers.
This design also resolves the possible deadlock mentioned above.


Changes from v2:
- Rebased on scsi-misc branche (commit 8c0eb596baa5)
- Minor addition to structure documentation in ufshcd.h
Changes from v1
- Allocate a tag for device management commands dynamically instead
  of reserving tag[MAX_QUEUE - 1].
- Changed the internal_cmd naming to dev_cmd to avoid confusion
  with other type of internal commands other than NOP and QUERY.

Dolev Raviv (1):
  scsi: ufs: Set fDeviceInit flag to initiate device initialization

Sujit Reddy Thumma (1):
  scsi: ufs: Add support for sending NOP OUT UPIU

 drivers/scsi/ufs/ufs.h|  127 ++-
 drivers/scsi/ufs/ufshcd.c |  886 +++--
 drivers/scsi/ufs/ufshcd.h |   43 ++-
 drivers/scsi/ufs/ufshci.h |2 +-
 4 files changed, 939 insertions(+), 119 deletions(-)

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

--
To unsubscribe from this list: send the line unsubscribe linux-arm-msm in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU

2013-07-09 Thread Sujit Reddy Thumma
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.

A tag is acquired from the SCSI tag map space in order to send the device
management command. When the tag is acquired by internal command the scsi
command is rejected with host busy flag in order to requeue the request.
To avoid frequent collisions between internal commands and scsi commands
the device management command tag is allocated in the opposite direction
w.r.t block layer tag allocation.

Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
Signed-off-by: Dolev Raviv dra...@codeaurora.org
---
 drivers/scsi/ufs/ufs.h|   43 +++-
 drivers/scsi/ufs/ufshcd.c |  596 +
 drivers/scsi/ufs/ufshcd.h |   29 ++-
 3 files changed, 552 insertions(+), 116 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 139bc06..14c0a4e 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -36,10 +36,16 @@
 #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 UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
-   ((byte3  24) | (byte2  16) |\
+   cpu_to_be32((byte3  24) | (byte2  16) |\
 (byte1  8) | (byte0))
 
 /*
@@ -73,6 +79,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 */
@@ -110,6 +117,12 @@ 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,
@@ -138,26 +151,32 @@ 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_req - general upiu request structure
+ * @header:UPIU header structure DW-0 to DW-2
+ * @sc: fields structure for scsi command DW-3 to DW-7
+ */
+struct utp_upiu_req {
+   struct utp_upiu_header header;
+   struct utp_upiu_cmd sc;
+};
+
+/**
+ * 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 +184,16 @@ struct utp_upiu_rsp {
 };
 
 /**
+ * struct utp_upiu_rsp - general upiu response structure
+ * @header: UPIU header structure DW-0 to DW-2
+ * @sr: fields structure for scsi command DW-3 to DW-12
+ */
+struct utp_upiu_rsp {
+   struct utp_upiu_header header;
+   struct utp_cmd_rsp sr;
+};
+
+/**
  * struct utp_upiu_task_req - Task request UPIU structure
  * @header - UPIU header structure DW0 to DW-2
  * @input_param1: Input parameter 1 DW-3
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index b743bd6..3f482b6 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -43,6 +43,11 @@
 /* UIC command timeout, unit: ms */
 #define UIC_CMD_TIMEOUT500
 
+/* NOP OUT retries waiting for NOP IN response */
+#define NOP_OUT_RETRIES10
+/* Timeout after 30 msecs if NOP OUT hangs without response */
+#define NOP_OUT_TIMEOUT30 /* msecs */
+
 enum {
UFSHCD_MAX_CHANNEL  = 0,
UFSHCD_MAX_ID   = 1,
@@ -71,6 +76,47 @@ 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 

[PATCH V3 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization

2013-07-09 Thread Sujit Reddy Thumma
From: Dolev Raviv dra...@codeaurora.org

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 dra...@codeaurora.org
Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
---
 drivers/scsi/ufs/ufs.h|   88 +-
 drivers/scsi/ufs/ufshcd.c |  292 -
 drivers/scsi/ufs/ufshcd.h |   14 ++
 drivers/scsi/ufs/ufshci.h |2 +-
 4 files changed, 390 insertions(+), 6 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 14c0a4e..db5bde4 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -43,6 +43,8 @@
 #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 UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
cpu_to_be32((byte3  24) | (byte2  16) |\
@@ -68,7 +70,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 */
@@ -97,8 +99,19 @@ enum {
UPIU_TASK_ATTR_ACA  = 0x03,
 };
 
-/* UTP QUERY Transaction Specific Fields OpCode */
+/* UPIU Query request function */
 enum {
+   UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01,
+   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 query_opcode {
UPIU_QUERY_OPCODE_NOP   = 0x0,
UPIU_QUERY_OPCODE_READ_DESC = 0x1,
UPIU_QUERY_OPCODE_WRITE_DESC= 0x2,
@@ -110,6 +123,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,
@@ -127,6 +155,7 @@ enum {
MASK_SCSI_STATUS= 0xFF,
MASK_TASK_RESPONSE  = 0xFF00,
MASK_RSP_UPIU_RESULT= 0x,
+   MASK_QUERY_DATA_SEG_LEN = 0x,
 };
 
 /* Task management service response */
@@ -160,13 +189,40 @@ struct utp_upiu_cmd {
 };
 
 /**
+ * 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-5
+ * @reserved: spec reserved DW-6,7
+ */
+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 DW-3 to DW-7
+ * @qr: fields structure for query request DW-3 to DW-7
  */
 struct utp_upiu_req {
struct utp_upiu_header header;
-   struct utp_upiu_cmd sc;
+   union {
+   struct utp_upiu_cmd sc;
+   struct utp_upiu_query qr;
+   };
 };
 
 /**
@@ -187,10 +243,14 @@ struct utp_cmd_rsp {
  * struct utp_upiu_rsp - general upiu response structure
  * @header: UPIU header structure DW-0 to DW-2
  * @sr: fields structure for scsi command DW-3 to DW-12
+ * @qr: fields structure for query request DW-3 to DW-7
  */
 struct utp_upiu_rsp {
struct utp_upiu_header header;
-   struct utp_cmd_rsp sr;
+   union {
+   struct utp_cmd_rsp sr;
+   struct utp_upiu_query qr;
+   };
 };
 
 /**
@@ -223,4 

[PATCH V3 2/4] scsi: ufs: Fix hardware race conditions while aborting a command

2013-07-09 Thread Sujit Reddy Thumma
There is a possible race condition in the hardware when the abort
command is issued to terminate the ongoing SCSI command as described
below:

- A bit in the door-bell register is set in the controller for a
  new SCSI command.
- In some rare situations, before controller get a chance to issue
  the command to the device, the software issued an abort command.
- If the device recieves abort command first then it returns success
  because the command itself is not present.
- Now if the controller commits the command to device it will be
  processed.
- Software thinks that command is aborted and proceed while still
  the device is processing it.
- The software, controller and device may go out of sync because of
  this race condition.

To avoid this, query task presence in the device before sending abort
task command so that after the abort operation, the command is guaranteed
to be non-existent in both controller and the device.

Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
---
 drivers/scsi/ufs/ufshcd.c |   70 +++-
 1 files changed, 55 insertions(+), 15 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a176421..51ce096 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2550,6 +2550,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd)
  * ufshcd_abort - abort a specific command
  * @cmd: SCSI command pointer
  *
+ * Abort the pending command in device by sending UFS_ABORT_TASK task 
management
+ * command, and in host controller by clearing the door-bell register. There 
can
+ * be race between controller sending the command to the device while abort is
+ * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is
+ * really issued and then try to abort it.
+ *
  * Returns SUCCESS/FAILED
  */
 static int ufshcd_abort(struct scsi_cmnd *cmd)
@@ -2558,7 +2564,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
struct ufs_hba *hba;
unsigned long flags;
unsigned int tag;
-   int err;
+   int err = 0;
+   int poll_cnt;
u8 resp;
struct ufshcd_lrb *lrbp;
 
@@ -2566,33 +2573,59 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
hba = shost_priv(host);
tag = cmd-request-tag;
 
-   spin_lock_irqsave(host-host_lock, flags);
+   /* If command is already aborted/completed, return SUCCESS */
+   if (!(test_bit(tag, hba-outstanding_reqs)))
+   goto out;
 
-   /* check if command is still pending */
-   if (!(test_bit(tag, hba-outstanding_reqs))) {
-   err = FAILED;
-   spin_unlock_irqrestore(host-host_lock, flags);
+   lrbp = hba-lrb[tag];
+   for (poll_cnt = 100; poll_cnt; poll_cnt--) {
+   err = ufshcd_issue_tm_cmd(hba, lrbp-lun, lrbp-task_tag,
+   UFS_QUERY_TASK, resp);
+   if (!err  resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) {
+   /* cmd pending in the device */
+   break;
+   } else if (!err  resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
+   u32 reg;
+
+   /*
+* cmd not pending in the device, check if it is
+* in transition.
+*/
+   reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+   if (reg  (1  tag)) {
+   /* sleep for max. 2ms to stabilize */
+   usleep_range(1000, 2000);
+   continue;
+   }
+   /* command completed already */
+   goto out;
+   } else {
+   if (!err)
+   err = resp; /* service response error */
+   goto out;
+   }
+   }
+
+   if (!poll_cnt) {
+   err = -EBUSY;
goto out;
}
-   spin_unlock_irqrestore(host-host_lock, flags);
 
-   lrbp = hba-lrb[tag];
err = ufshcd_issue_tm_cmd(hba, lrbp-lun, lrbp-task_tag,
UFS_ABORT_TASK, resp);
if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
-   err = FAILED;
+   if (!err)
+   err = resp; /* service response error */
goto out;
-   } else {
-   err = SUCCESS;
}
 
+   err = ufshcd_clear_cmd(hba, tag);
+   if (err)
+   goto out;
+
scsi_dma_unmap(cmd);
 
spin_lock_irqsave(host-host_lock, flags);
-
-   /* clear the respective UTRLCLR register bit */
-   ufshcd_utrl_clear(hba, tag);
-
__clear_bit(tag, hba-outstanding_reqs);
hba-lrb[tag].cmd = NULL;
spin_unlock_irqrestore(host-host_lock, flags);
@@ -2600,6 +2633,13 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)

[PATCH V3 1/2] scsi: ufs: Add support for host assisted background operations

2013-07-09 Thread Sujit Reddy Thumma
Background operations in the UFS device can be disabled by
the host to reduce the response latency of transfer requests.
Add support for enabling/disabling the background operations
during runtime suspend/resume of the device.

If the device is in critical need of BKOPS it will raise an
URGENT_BKOPS exception which should be handled by the host to
make sure the device performs as expected.

During bootup, the BKOPS is enabled in the device by default.
The disable of BKOPS is supported only when the driver supports
runtime suspend/resume operations as the runtime PM framework
provides a way to determine the device idleness and hence BKOPS
can be managed effectively. During runtime resume the BKOPS is
disabled to reduce latency and during runtime suspend the BKOPS
is enabled to allow device to carry out idle time BKOPS.

In some cases where the BKOPS is disabled during runtime resume
and due to continuous data transfers the runtime suspend is not
triggered, the BKOPS is enabled when the device raises a level-2
exception (outstanding operations - performance impact).

Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
---
 drivers/scsi/ufs/ufs.h|   25 -
 drivers/scsi/ufs/ufshcd.c |  338 +
 drivers/scsi/ufs/ufshcd.h |   10 ++
 3 files changed, 372 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index db5bde4..549a652 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -107,7 +107,29 @@ enum {
 
 /* Flag idn for Query Requests*/
 enum flag_idn {
-   QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
+   QUERY_FLAG_IDN_FDEVICEINIT  = 0x01,
+   QUERY_FLAG_IDN_BKOPS_EN = 0x04,
+};
+
+/* Attribute idn for Query requests */
+enum attr_idn {
+   QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
+   QUERY_ATTR_IDN_EE_CONTROL   = 0x0D,
+   QUERY_ATTR_IDN_EE_STATUS= 0x0E,
+};
+
+/* Exception event mask values */
+enum {
+   MASK_EE_STATUS  = 0x,
+   MASK_EE_URGENT_BKOPS= (1  2),
+};
+
+/* Background operation status */
+enum {
+   BKOPS_STATUS_NO_OP   = 0x0,
+   BKOPS_STATUS_NON_CRITICAL= 0x1,
+   BKOPS_STATUS_PERF_IMPACT = 0x2,
+   BKOPS_STATUS_CRITICAL= 0x3,
 };
 
 /* UTP QUERY Transaction Specific Fields OpCode */
@@ -156,6 +178,7 @@ enum {
MASK_TASK_RESPONSE  = 0xFF00,
MASK_RSP_UPIU_RESULT= 0x,
MASK_QUERY_DATA_SEG_LEN = 0x,
+   MASK_RSP_EXCEPTION_EVENT = 0x1,
 };
 
 /* Task management service response */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 96ccb28..a25de66 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -268,6 +268,21 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp 
*ucd_rsp_ptr)
 }
 
 /**
+ * ufshcd_is_exception_event - Check if the device raised an exception event
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * The function checks if the device raised an exception event indicated in
+ * the Device Information field of response UPIU.
+ *
+ * Returns true if exception is raised, false otherwise.
+ */
+static inline bool ufshcd_is_exception_event(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+   return be32_to_cpu(ucd_rsp_ptr-header.dword_2) 
+   MASK_RSP_EXCEPTION_EVENT ? true : false;
+}
+
+/**
  * ufshcd_config_int_aggr - Configure interrupt aggregation values.
  * Currently there is no use case where we want to configure
  * interrupt aggregation dynamically. So to configure interrupt
@@ -1174,6 +1189,86 @@ out_no_mem:
 }
 
 /**
+ * ufshcd_query_attr - Helper function for composing attribute requests
+ * hba: per-adapter instance
+ * opcode: attribute opcode
+ * idn: attribute idn to access
+ * index: index field
+ * selector: selector field
+ * attr_val: the attribute value after the query request completes
+ *
+ * Returns 0 for success, non-zero in case of failure
+*/
+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 *query;
+   struct ufs_query_res *response;
+   int err = -ENOMEM;
+
+   if (!attr_val) {
+   dev_err(hba-dev, %s: attribute value required for write 
request\n,
+   __func__);
+   err = -EINVAL;
+   goto out;
+   }
+
+   query = kzalloc(sizeof(struct ufs_query_req), GFP_KERNEL);
+   if (!query) {
+   dev_err(hba-dev,
+   %s: Failed allocating ufs_query_req instance\n,
+   __func__);
+   goto out;
+   }
+
+   response = kzalloc(sizeof(struct ufs_query_res), GFP_KERNEL);
+   if (!response) {
+   dev_err(hba-dev,
+   %s: Failed allocating ufs_query_res instance\n,
+   __func__);
+  

[PATCH V3 0/4] scsi: ufs: Improve UFS error handling

2013-07-09 Thread Sujit Reddy Thumma
The first patch fixes many issues with current task management handling
in UFSHCD driver. Others improve error handling in various scenarios.

These patches depends on:
[PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
[PATCH V3 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization
[PATCH V3 1/2] scsi: ufs: Add support for host assisted background operations
[PATCH V3 2/2] scsi: ufs: Add runtime PM support for UFS host controller driver

Changes from v2:
- [PATCH V3 1/4]: Make the task management command task tag unique
  across SCSI/NOP/QUERY request tags.
- [PATCH V3 3/4]: While handling device/host reset, wait for
  pending fatal handler to return if running.
Changes from v1:
- [PATCH V2 1/4]: Fix a race condition because of overloading
  outstanding_tasks variable to lock the slots. A new variable
  tm_slots_in_use will track which slots are in use by the driver.
- [PATCH V2 2/4]: Commit text update to clarify the hardware race
  with more details.
- [PATCH V2 3/4]: Minor cleanup and rebase
- [PATCH V2 4/4]: Fix a bug - sleeping in atomic context

Sujit Reddy Thumma (4):
  scsi: ufs: Fix broken task management command implementation
  scsi: ufs: Fix hardware race conditions while aborting a command
  scsi: ufs: Fix device and host reset methods
  scsi: ufs: Improve UFS fatal error handling

 drivers/scsi/ufs/ufshcd.c | 1035 +
 drivers/scsi/ufs/ufshcd.h |   12 +-
 drivers/scsi/ufs/ufshci.h |   19 +-
 3 files changed, 873 insertions(+), 193 deletions(-)

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

--
To unsubscribe from this list: send the line unsubscribe linux-arm-msm in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH V3 3/4] scsi: ufs: Fix device and host reset methods

2013-07-09 Thread Sujit Reddy Thumma
As of now SCSI initiated error handling is broken because,
the reset APIs don't try to bring back the device initialized and
ready for further transfers.

In case of timeouts, the scsi error handler takes care of handling aborts
and resets. Improve the error handling in such scenario by resetting the
device and host and re-initializing them in proper manner.

Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
---
 drivers/scsi/ufs/ufshcd.c |  467 +++--
 drivers/scsi/ufs/ufshcd.h |2 +
 2 files changed, 411 insertions(+), 58 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 51ce096..b4c9910 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -69,9 +69,15 @@ enum {
 
 /* UFSHCD states */
 enum {
-   UFSHCD_STATE_OPERATIONAL,
UFSHCD_STATE_RESET,
UFSHCD_STATE_ERROR,
+   UFSHCD_STATE_OPERATIONAL,
+};
+
+/* UFSHCD error handling flags */
+enum {
+   UFSHCD_EH_HOST_RESET_PENDING = (1  0),
+   UFSHCD_EH_DEVICE_RESET_PENDING = (1  1),
 };
 
 /* Interrupt configuration options */
@@ -87,6 +93,22 @@ enum {
INT_AGGR_CONFIG,
 };
 
+#define ufshcd_set_device_reset_pending(h) \
+   (h-eh_flags |= UFSHCD_EH_DEVICE_RESET_PENDING)
+#define ufshcd_set_host_reset_pending(h) \
+   (h-eh_flags |= UFSHCD_EH_HOST_RESET_PENDING)
+#define ufshcd_device_reset_pending(h) \
+   (h-eh_flags  UFSHCD_EH_DEVICE_RESET_PENDING)
+#define ufshcd_host_reset_pending(h) \
+   (h-eh_flags  UFSHCD_EH_HOST_RESET_PENDING)
+#define ufshcd_clear_device_reset_pending(h) \
+   (h-eh_flags = ~UFSHCD_EH_DEVICE_RESET_PENDING)
+#define ufshcd_clear_host_reset_pending(h) \
+   (h-eh_flags = ~UFSHCD_EH_HOST_RESET_PENDING)
+
+static void ufshcd_tmc_handler(struct ufs_hba *hba);
+static void ufshcd_async_scan(void *data, async_cookie_t cookie);
+
 /*
  * ufshcd_wait_for_register - wait for register value to change
  * @hba - per-adapter interface
@@ -851,9 +873,22 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *cmd)
 
tag = cmd-request-tag;
 
-   if (hba-ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
+   switch (hba-ufshcd_state) {
+   case UFSHCD_STATE_OPERATIONAL:
+   break;
+   case UFSHCD_STATE_RESET:
err = SCSI_MLQUEUE_HOST_BUSY;
goto out;
+   case UFSHCD_STATE_ERROR:
+   set_host_byte(cmd, DID_ERROR);
+   cmd-scsi_done(cmd);
+   goto out;
+   default:
+   dev_WARN_ONCE(hba-dev, 1, %s: invalid state %d\n,
+   __func__, hba-ufshcd_state);
+   set_host_byte(cmd, DID_BAD_TARGET);
+   cmd-scsi_done(cmd);
+   goto out;
}
 
/* acquire the tag to make sure device cmds don't use it */
@@ -1573,8 +1608,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba 
*hba)
if (hba-ufshcd_state == UFSHCD_STATE_RESET)
scsi_unblock_requests(hba-host);
 
-   hba-ufshcd_state = UFSHCD_STATE_OPERATIONAL;
-
 out:
return err;
 }
@@ -2273,6 +2306,106 @@ out:
 }
 
 /**
+ * ufshcd_utrl_is_rsr_enabled - check if run-stop register is enabled
+ * @hba: per-adapter instance
+ */
+static bool ufshcd_utrl_is_rsr_enabled(struct ufs_hba *hba)
+{
+   return ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_LIST_RUN_STOP)  0x1;
+}
+
+/**
+ * ufshcd_utmrl_is_rsr_enabled - check if run-stop register is enabled
+ * @hba: per-adapter instance
+ */
+static bool ufshcd_utmrl_is_rsr_enabled(struct ufs_hba *hba)
+{
+   return ufshcd_readl(hba, REG_UTP_TASK_REQ_LIST_RUN_STOP)  0x1;
+}
+
+/**
+ * ufshcd_complete_pending_tasks - complete outstanding tasks
+ * @hba: per adapter instance
+ *
+ * Abort in-progress task management commands and wakeup
+ * waiting threads.
+ *
+ * Returns non-zero error value when failed to clear all the commands.
+ */
+static int ufshcd_complete_pending_tasks(struct ufs_hba *hba)
+{
+   u32 reg;
+   int err = 0;
+   unsigned long flags;
+
+   if (!hba-outstanding_tasks)
+   goto out;
+
+   /* Clear UTMRL only when run-stop is enabled */
+   if (ufshcd_utmrl_is_rsr_enabled(hba))
+   ufshcd_writel(hba, ~hba-outstanding_tasks,
+   REG_UTP_TASK_REQ_LIST_CLEAR);
+
+   /* poll for max. 1 sec to clear door bell register by h/w */
+   reg = ufshcd_wait_for_register(hba,
+   REG_UTP_TASK_REQ_DOOR_BELL,
+   hba-outstanding_tasks, 0, 1000, 1000);
+   if (reg  hba-outstanding_tasks)
+   err = -ETIMEDOUT;
+
+   spin_lock_irqsave(hba-host-host_lock, flags);
+   /* complete commands that were cleared out */
+   ufshcd_tmc_handler(hba);
+   spin_unlock_irqrestore(hba-host-host_lock, flags);
+out:
+   if (err)
+   dev_err(hba-dev, %s: failed, still pending = 0x%.8x\n,
+  

[PATCH V3 1/4] scsi: ufs: Fix broken task management command implementation

2013-07-09 Thread Sujit Reddy Thumma
Currently, sending Task Management (TM) command to the card might
be broken in some scenarios as listed below:

Problem: If there are more than 8 TM commands the implementation
 returns error to the caller.
Fix: Wait for one of the slots to be emptied and send the command.

Problem: Sometimes it is necessary for the caller to know the TM service
 response code to determine the task status.
Fix: Propogate the service response to the caller.

Problem: If the TM command times out no proper error recovery is
 implemented.
Fix: Clear the command in the controller door-bell register, so that
 further commands for the same slot don't fail.

Problem: While preparing the TM command descriptor, the task tag used
 should be unique across SCSI/NOP/QUERY/TM commands and not the
 task tag of the command which the TM command is trying to manage.
Fix: Use a unique task tag instead of task tag of SCSI command.

Problem: Since the TM command involves H/W communication, abruptly ending
 the request on kill interrupt signal might cause h/w malfunction.
Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE
 set.

Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
---
 drivers/scsi/ufs/ufshcd.c |  177 ++---
 drivers/scsi/ufs/ufshcd.h |8 ++-
 2 files changed, 126 insertions(+), 59 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index af7d01d..a176421 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -53,6 +53,9 @@
 /* Query request timeout */
 #define QUERY_REQ_TIMEOUT 30 /* msec */
 
+/* Task management command timeout */
+#define TM_CMD_TIMEOUT 100 /* msecs */
+
 /* Expose the flag value from utp_upiu_query.value */
 #define MASK_QUERY_UPIU_FLAG_LOC 0xFF
 
@@ -190,13 +193,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc 
*task_req_descp)
 /**
  * ufshcd_get_tm_free_slot - get a free slot for task management request
  * @hba: per adapter instance
+ * @free_slot: pointer to variable with available slot value
  *
- * Returns maximum number of task management request slots in case of
- * task management queue full or returns the free slot number
+ * Get a free tag and lock it until ufshcd_put_tm_slot() is called.
+ * Returns 0 if free slot is not available, else return 1 with tag value
+ * in @free_slot.
  */
-static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba)
+static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot)
+{
+   int tag;
+   bool ret = false;
+
+   if (!free_slot)
+   goto out;
+
+   do {
+   tag = find_first_zero_bit(hba-tm_slots_in_use, hba-nutmrs);
+   if (tag = hba-nutmrs)
+   goto out;
+   } while (test_and_set_bit_lock(tag, hba-tm_slots_in_use));
+
+   *free_slot = tag;
+   ret = true;
+out:
+   return ret;
+}
+
+static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot)
 {
-   return find_first_zero_bit(hba-outstanding_tasks, hba-nutmrs);
+   clear_bit_unlock(slot, hba-tm_slots_in_use);
 }
 
 /**
@@ -1778,10 +1803,11 @@ static void ufshcd_slave_destroy(struct scsi_device 
*sdev)
  * ufshcd_task_req_compl - handle task management request completion
  * @hba: per adapter instance
  * @index: index of the completed request
+ * @resp: task management service response
  *
- * Returns SUCCESS/FAILED
+ * Returns non-zero value on error, zero on success
  */
-static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index)
+static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp)
 {
struct utp_task_req_desc *task_req_descp;
struct utp_upiu_task_rsp *task_rsp_upiup;
@@ -1802,19 +1828,15 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, 
u32 index)
task_req_descp[index].task_rsp_upiu;
task_result = be32_to_cpu(task_rsp_upiup-header.dword_1);
task_result = ((task_result  MASK_TASK_RESPONSE)  8);
-
-   if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL 
-   task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED)
-   task_result = FAILED;
-   else
-   task_result = SUCCESS;
+   if (resp)
+   *resp = (u8)task_result;
} else {
-   task_result = FAILED;
-   dev_err(hba-dev,
-   trc: Invalid ocs = %x\n, ocs_value);
+   dev_err(hba-dev, %s: failed, ocs = 0x%x\n,
+   __func__, ocs_value);
}
spin_unlock_irqrestore(hba-host-host_lock, flags);
-   return task_result;
+
+   return ocs_value;
 }
 
 /**
@@ -2298,7 +2320,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba)
 
tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
hba-tm_condition = 

[PATCH V3 2/2] scsi: ufs: Add runtime PM support for UFS host controller driver

2013-07-09 Thread Sujit Reddy Thumma
Add runtime PM helpers to suspend/resume UFS controller at runtime.
Enable runtime PM by default for pci and platform drivers as the
initialized hardware can suspend if it is not used after bootup.

Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
---
 drivers/scsi/ufs/ufshcd-pci.c|   65 ++---
 drivers/scsi/ufs/ufshcd-pltfrm.c |   51 +-
 drivers/scsi/ufs/ufshcd.c|8 +
 3 files changed, 117 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
index 48be39a..7bd8faa 100644
--- a/drivers/scsi/ufs/ufshcd-pci.c
+++ b/drivers/scsi/ufs/ufshcd-pci.c
@@ -35,6 +35,7 @@
 
 #include ufshcd.h
 #include linux/pci.h
+#include linux/pm_runtime.h
 
 #ifdef CONFIG_PM
 /**
@@ -44,7 +45,7 @@
  *
  * Returns -ENOSYS
  */
-static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+static int ufshcd_pci_suspend(struct device *dev)
 {
/*
 * TODO:
@@ -61,7 +62,7 @@ static int ufshcd_pci_suspend(struct pci_dev *pdev, 
pm_message_t state)
  *
  * Returns -ENOSYS
  */
-static int ufshcd_pci_resume(struct pci_dev *pdev)
+static int ufshcd_pci_resume(struct device *dev)
 {
/*
 * TODO:
@@ -71,8 +72,48 @@ static int ufshcd_pci_resume(struct pci_dev *pdev)
 
return -ENOSYS;
 }
+#else
+#define ufshcd_pci_suspend NULL
+#define ufshcd_pci_resume  NULL
 #endif /* CONFIG_PM */
 
+#ifdef CONFIG_PM_RUNTIME
+static int ufshcd_pci_runtime_suspend(struct device *dev)
+{
+   struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+   struct ufs_hba *hba = pci_get_drvdata(pdev);
+
+   if (!hba)
+   return 0;
+
+   return ufshcd_runtime_suspend(hba);
+}
+static int ufshcd_pci_runtime_resume(struct device *dev)
+{
+   struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+   struct ufs_hba *hba = pci_get_drvdata(pdev);
+
+   if (!hba)
+   return 0;
+
+   return ufshcd_runtime_resume(hba);
+}
+static int ufshcd_pci_runtime_idle(struct device *dev)
+{
+   struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+   struct ufs_hba *hba = pci_get_drvdata(pdev);
+
+   if (!hba)
+   return 0;
+
+   return ufshcd_runtime_idle(hba);
+}
+#else /* !CONFIG_PM_RUNTIME */
+#define ufshcd_pci_runtime_suspend NULL
+#define ufshcd_pci_runtime_resume  NULL
+#define ufshcd_pci_runtime_idleNULL
+#endif /* CONFIG_PM_RUNTIME */
+
 /**
  * ufshcd_pci_shutdown - main function to put the controller in reset state
  * @pdev: pointer to PCI device handle
@@ -91,6 +132,9 @@ static void ufshcd_pci_remove(struct pci_dev *pdev)
 {
struct ufs_hba *hba = pci_get_drvdata(pdev);
 
+   pm_runtime_forbid(pdev-dev);
+   pm_runtime_get_noresume(pdev-dev);
+
disable_irq(pdev-irq);
ufshcd_remove(hba);
pci_release_regions(pdev);
@@ -168,6 +212,8 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct 
pci_device_id *id)
}
 
pci_set_drvdata(pdev, hba);
+   pm_runtime_put_noidle(pdev-dev);
+   pm_runtime_allow(pdev-dev);
 
return 0;
 
@@ -182,6 +228,14 @@ out_error:
return err;
 }
 
+static const struct dev_pm_ops ufshcd_pci_pm_ops = {
+   .suspend= ufshcd_pci_suspend,
+   .resume = ufshcd_pci_resume,
+   .runtime_suspend = ufshcd_pci_runtime_suspend,
+   .runtime_resume  = ufshcd_pci_runtime_resume,
+   .runtime_idle= ufshcd_pci_runtime_idle,
+};
+
 static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = {
{ PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ } /* terminate list */
@@ -195,10 +249,9 @@ static struct pci_driver ufshcd_pci_driver = {
.probe = ufshcd_pci_probe,
.remove = ufshcd_pci_remove,
.shutdown = ufshcd_pci_shutdown,
-#ifdef CONFIG_PM
-   .suspend = ufshcd_pci_suspend,
-   .resume = ufshcd_pci_resume,
-#endif
+   .driver = {
+   .pm = ufshcd_pci_pm_ops
+   },
 };
 
 module_pci_driver(ufshcd_pci_driver);
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index c42db40..b1f2605 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -34,6 +34,7 @@
  */
 
 #include linux/platform_device.h
+#include linux/pm_runtime.h
 
 #include ufshcd.h
 
@@ -87,6 +88,43 @@ static int ufshcd_pltfrm_resume(struct device *dev)
 #define ufshcd_pltfrm_resume   NULL
 #endif
 
+#ifdef CONFIG_PM_RUNTIME
+static int ufshcd_pltfrm_runtime_suspend(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct ufs_hba *hba =  platform_get_drvdata(pdev);
+
+   if (!hba)
+   return 0;
+
+   return ufshcd_runtime_suspend(hba);
+}
+static int ufshcd_pltfrm_runtime_resume(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
+   struct 

[PATCH V3 4/4] scsi: ufs: Improve UFS fatal error handling

2013-07-09 Thread Sujit Reddy Thumma
Error handling in UFS driver is broken and resets the host controller
for fatal errors without re-initialization. Correct the fatal error
handling sequence according to UFS Host Controller Interface (HCI)
v1.1 specification.

o Upon determining fatal error condition the host controller may hang
  forever until a reset is applied, so just retrying the command doesn't
  work without a reset. So, the reset is applied in the driver context
  in a separate work and SCSI mid-layer isn't informed until reset is
  applied.

o Processed requests which are completed without error are reported to
  SCSI layer as successful and any pending commands that are not started
  yet or are not cause of the error are re-queued into scsi midlayer queue.
  For the command that caused error, host controller or device is reset
  and DID_ERROR is returned for command retry after applying reset.

o SCSI is informed about the expected Unit-Attentioni exception from the
  device for the immediate command after a reset so that the SCSI layer
  take necessary steps to establish communication with the device.

Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
---
 drivers/scsi/ufs/ufshcd.c |  349 +++-
 drivers/scsi/ufs/ufshcd.h |2 +
 drivers/scsi/ufs/ufshci.h |   19 ++-
 3 files changed, 295 insertions(+), 75 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index b4c9910..2a3874f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -80,6 +80,14 @@ enum {
UFSHCD_EH_DEVICE_RESET_PENDING = (1  1),
 };
 
+/* UFSHCD UIC layer error flags */
+enum {
+   UFSHCD_UIC_DL_PA_INIT_ERROR = (1  0), /* Data link layer error */
+   UFSHCD_UIC_NL_ERROR = (1  1), /* Network layer error */
+   UFSHCD_UIC_TL_ERROR = (1  2), /* Transport Layer error */
+   UFSHCD_UIC_DME_ERROR = (1  3), /* DME error */
+};
+
 /* Interrupt configuration options */
 enum {
UFSHCD_INT_DISABLE,
@@ -108,6 +116,7 @@ enum {
 
 static void ufshcd_tmc_handler(struct ufs_hba *hba);
 static void ufshcd_async_scan(void *data, async_cookie_t cookie);
+static int ufshcd_reset_and_restore(struct ufs_hba *hba);
 
 /*
  * ufshcd_wait_for_register - wait for register value to change
@@ -1605,9 +1614,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba 
*hba)
goto out;
}
 
-   if (hba-ufshcd_state == UFSHCD_STATE_RESET)
-   scsi_unblock_requests(hba-host);
-
 out:
return err;
 }
@@ -1733,66 +1739,6 @@ static int ufshcd_validate_dev_connection(struct ufs_hba 
*hba)
 }
 
 /**
- * ufshcd_do_reset - reset the host controller
- * @hba: per adapter instance
- *
- * Returns SUCCESS/FAILED
- */
-static int ufshcd_do_reset(struct ufs_hba *hba)
-{
-   struct ufshcd_lrb *lrbp;
-   unsigned long flags;
-   int tag;
-
-   /* block commands from midlayer */
-   scsi_block_requests(hba-host);
-
-   spin_lock_irqsave(hba-host-host_lock, flags);
-   hba-ufshcd_state = UFSHCD_STATE_RESET;
-
-   /* send controller to reset state */
-   ufshcd_hba_stop(hba);
-   spin_unlock_irqrestore(hba-host-host_lock, flags);
-
-   /* abort outstanding commands */
-   for (tag = 0; tag  hba-nutrs; tag++) {
-   if (test_bit(tag, hba-outstanding_reqs)) {
-   lrbp = hba-lrb[tag];
-   if (lrbp-cmd) {
-   scsi_dma_unmap(lrbp-cmd);
-   lrbp-cmd-result = DID_RESET  16;
-   lrbp-cmd-scsi_done(lrbp-cmd);
-   lrbp-cmd = NULL;
-   clear_bit_unlock(tag, hba-lrb_in_use);
-   }
-   }
-   }
-
-   /* complete device management command */
-   if (hba-dev_cmd.complete)
-   complete(hba-dev_cmd.complete);
-
-   /* clear outstanding request/task bit maps */
-   hba-outstanding_reqs = 0;
-   hba-outstanding_tasks = 0;
-
-   /* 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;
-}
-
-/**
  * ufshcd_slave_alloc - handle initial SCSI device configurations
  * @sdev: pointer to SCSI device
  *
@@ -1809,6 +1755,9 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev)
sdev-use_10_for_ms = 1;
scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
 
+   /* allow SCSI layer to restart the device in case of errors */
+   sdev-allow_restart = 1;
+
/*
 * Inform SCSI Midlayer that the LUN queue depth is same as the
 * controller queue depth. If a LUN queue depth is less than the
@@ 

Re: [PATCH V3 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization

2013-07-09 Thread merez
Tested-by: Maya Erez me...@codeaurora.org

 From: Dolev Raviv dra...@codeaurora.org

 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 dra...@codeaurora.org
 Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
 ---
  drivers/scsi/ufs/ufs.h|   88 +-
  drivers/scsi/ufs/ufshcd.c |  292
 -
  drivers/scsi/ufs/ufshcd.h |   14 ++
  drivers/scsi/ufs/ufshci.h |2 +-
  4 files changed, 390 insertions(+), 6 deletions(-)

 diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
 index 14c0a4e..db5bde4 100644
 --- a/drivers/scsi/ufs/ufs.h
 +++ b/drivers/scsi/ufs/ufs.h
 @@ -43,6 +43,8 @@
  #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 UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
   cpu_to_be32((byte3  24) | (byte2  16) |\
 @@ -68,7 +70,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 */
 @@ -97,8 +99,19 @@ enum {
   UPIU_TASK_ATTR_ACA  = 0x03,
  };

 -/* UTP QUERY Transaction Specific Fields OpCode */
 +/* UPIU Query request function */
  enum {
 + UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01,
 + 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 query_opcode {
   UPIU_QUERY_OPCODE_NOP   = 0x0,
   UPIU_QUERY_OPCODE_READ_DESC = 0x1,
   UPIU_QUERY_OPCODE_WRITE_DESC= 0x2,
 @@ -110,6 +123,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,
 @@ -127,6 +155,7 @@ enum {
   MASK_SCSI_STATUS= 0xFF,
   MASK_TASK_RESPONSE  = 0xFF00,
   MASK_RSP_UPIU_RESULT= 0x,
 + MASK_QUERY_DATA_SEG_LEN = 0x,
  };

  /* Task management service response */
 @@ -160,13 +189,40 @@ struct utp_upiu_cmd {
  };

  /**
 + * 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-5
 + * @reserved: spec reserved DW-6,7
 + */
 +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 DW-3 to DW-7
 + * @qr: fields structure for query request DW-3 to DW-7
   */
  struct utp_upiu_req {
   struct utp_upiu_header header;
 - struct utp_upiu_cmd sc;
 + union {
 + struct utp_upiu_cmd sc;
 + struct utp_upiu_query qr;
 + };
  };

  /**
 @@ -187,10 +243,14 @@ struct utp_cmd_rsp {
   * struct utp_upiu_rsp - general upiu response structure
   * @header: UPIU header structure DW-0 to DW-2
   * @sr: fields structure for scsi command DW-3 to DW-12
 + * @qr: fields structure for query request DW-3 to DW-7
   */
  struct utp_upiu_rsp {
   struct utp_upiu_header header;
 - struct utp_cmd_rsp sr;
 + union {
 + struct utp_cmd_rsp sr;
 + 

Re: [PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU

2013-07-09 Thread merez
Tested-by: Maya Erez me...@codeaurora.org

 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.

 A tag is acquired from the SCSI tag map space in order to send the device
 management command. When the tag is acquired by internal command the scsi
 command is rejected with host busy flag in order to requeue the request.
 To avoid frequent collisions between internal commands and scsi commands
 the device management command tag is allocated in the opposite direction
 w.r.t block layer tag allocation.

 Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
 Signed-off-by: Dolev Raviv dra...@codeaurora.org
 ---
  drivers/scsi/ufs/ufs.h|   43 +++-
  drivers/scsi/ufs/ufshcd.c |  596
 +
  drivers/scsi/ufs/ufshcd.h |   29 ++-
  3 files changed, 552 insertions(+), 116 deletions(-)

 diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
 index 139bc06..14c0a4e 100644
 --- a/drivers/scsi/ufs/ufs.h
 +++ b/drivers/scsi/ufs/ufs.h
 @@ -36,10 +36,16 @@
  #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 UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
 - ((byte3  24) | (byte2  16) |\
 + cpu_to_be32((byte3  24) | (byte2  16) |\
(byte1  8) | (byte0))

  /*
 @@ -73,6 +79,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 */
 @@ -110,6 +117,12 @@ 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,
 @@ -138,26 +151,32 @@ 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_req - general upiu request structure
 + * @header:UPIU header structure DW-0 to DW-2
 + * @sc: fields structure for scsi command DW-3 to DW-7
 + */
 +struct utp_upiu_req {
 + struct utp_upiu_header header;
 + struct utp_upiu_cmd sc;
 +};
 +
 +/**
 + * 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 +184,16 @@ struct utp_upiu_rsp {
  };

  /**
 + * struct utp_upiu_rsp - general upiu response structure
 + * @header: UPIU header structure DW-0 to DW-2
 + * @sr: fields structure for scsi command DW-3 to DW-12
 + */
 +struct utp_upiu_rsp {
 + struct utp_upiu_header header;
 + struct utp_cmd_rsp sr;
 +};
 +
 +/**
   * struct utp_upiu_task_req - Task request UPIU structure
   * @header - UPIU header structure DW0 to DW-2
   * @input_param1: Input parameter 1 DW-3
 diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
 index b743bd6..3f482b6 100644
 --- a/drivers/scsi/ufs/ufshcd.c
 +++ b/drivers/scsi/ufs/ufshcd.c
 @@ -43,6 +43,11 @@
  /* UIC command timeout, unit: ms */
  #define UIC_CMD_TIMEOUT  500

 +/* NOP OUT retries waiting for NOP IN response */
 +#define NOP_OUT_RETRIES10
 +/* Timeout after 30 msecs if NOP OUT hangs without response */
 +#define NOP_OUT_TIMEOUT30 /* msecs */
 +
  enum {
   UFSHCD_MAX_CHANNEL  = 0,
   UFSHCD_MAX_ID   = 1,
 @@ -71,6 +76,47 @@ 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
 + * 

Re: [PATCH V3 1/2] scsi: ufs: Add support for host assisted background operations

2013-07-09 Thread merez
Tested-by: Maya Erez me...@codeaurora.org

 Background operations in the UFS device can be disabled by
 the host to reduce the response latency of transfer requests.
 Add support for enabling/disabling the background operations
 during runtime suspend/resume of the device.

 If the device is in critical need of BKOPS it will raise an
 URGENT_BKOPS exception which should be handled by the host to
 make sure the device performs as expected.

 During bootup, the BKOPS is enabled in the device by default.
 The disable of BKOPS is supported only when the driver supports
 runtime suspend/resume operations as the runtime PM framework
 provides a way to determine the device idleness and hence BKOPS
 can be managed effectively. During runtime resume the BKOPS is
 disabled to reduce latency and during runtime suspend the BKOPS
 is enabled to allow device to carry out idle time BKOPS.

 In some cases where the BKOPS is disabled during runtime resume
 and due to continuous data transfers the runtime suspend is not
 triggered, the BKOPS is enabled when the device raises a level-2
 exception (outstanding operations - performance impact).

 Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
 ---
  drivers/scsi/ufs/ufs.h|   25 -
  drivers/scsi/ufs/ufshcd.c |  338
 +
  drivers/scsi/ufs/ufshcd.h |   10 ++
  3 files changed, 372 insertions(+), 1 deletions(-)

 diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
 index db5bde4..549a652 100644
 --- a/drivers/scsi/ufs/ufs.h
 +++ b/drivers/scsi/ufs/ufs.h
 @@ -107,7 +107,29 @@ enum {

  /* Flag idn for Query Requests*/
  enum flag_idn {
 - QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
 + QUERY_FLAG_IDN_FDEVICEINIT  = 0x01,
 + QUERY_FLAG_IDN_BKOPS_EN = 0x04,
 +};
 +
 +/* Attribute idn for Query requests */
 +enum attr_idn {
 + QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
 + QUERY_ATTR_IDN_EE_CONTROL   = 0x0D,
 + QUERY_ATTR_IDN_EE_STATUS= 0x0E,
 +};
 +
 +/* Exception event mask values */
 +enum {
 + MASK_EE_STATUS  = 0x,
 + MASK_EE_URGENT_BKOPS= (1  2),
 +};
 +
 +/* Background operation status */
 +enum {
 + BKOPS_STATUS_NO_OP   = 0x0,
 + BKOPS_STATUS_NON_CRITICAL= 0x1,
 + BKOPS_STATUS_PERF_IMPACT = 0x2,
 + BKOPS_STATUS_CRITICAL= 0x3,
  };

  /* UTP QUERY Transaction Specific Fields OpCode */
 @@ -156,6 +178,7 @@ enum {
   MASK_TASK_RESPONSE  = 0xFF00,
   MASK_RSP_UPIU_RESULT= 0x,
   MASK_QUERY_DATA_SEG_LEN = 0x,
 + MASK_RSP_EXCEPTION_EVENT = 0x1,
  };

  /* Task management service response */
 diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
 index 96ccb28..a25de66 100644
 --- a/drivers/scsi/ufs/ufshcd.c
 +++ b/drivers/scsi/ufs/ufshcd.c
 @@ -268,6 +268,21 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp
 *ucd_rsp_ptr)
  }

  /**
 + * ufshcd_is_exception_event - Check if the device raised an exception
 event
 + * @ucd_rsp_ptr: pointer to response UPIU
 + *
 + * The function checks if the device raised an exception event indicated
 in
 + * the Device Information field of response UPIU.
 + *
 + * Returns true if exception is raised, false otherwise.
 + */
 +static inline bool ufshcd_is_exception_event(struct utp_upiu_rsp
 *ucd_rsp_ptr)
 +{
 + return be32_to_cpu(ucd_rsp_ptr-header.dword_2) 
 + MASK_RSP_EXCEPTION_EVENT ? true : false;
 +}
 +
 +/**
   * ufshcd_config_int_aggr - Configure interrupt aggregation values.
   *   Currently there is no use case where we want to configure
   *   interrupt aggregation dynamically. So to configure interrupt
 @@ -1174,6 +1189,86 @@ out_no_mem:
  }

  /**
 + * ufshcd_query_attr - Helper function for composing attribute requests
 + * hba: per-adapter instance
 + * opcode: attribute opcode
 + * idn: attribute idn to access
 + * index: index field
 + * selector: selector field
 + * attr_val: the attribute value after the query request completes
 + *
 + * Returns 0 for success, non-zero in case of failure
 +*/
 +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 *query;
 + struct ufs_query_res *response;
 + int err = -ENOMEM;
 +
 + if (!attr_val) {
 + dev_err(hba-dev, %s: attribute value required for write 
 request\n,
 + __func__);
 + err = -EINVAL;
 + goto out;
 + }
 +
 + query = kzalloc(sizeof(struct ufs_query_req), GFP_KERNEL);
 + if (!query) {
 + dev_err(hba-dev,
 + %s: Failed allocating ufs_query_req instance\n,
 + __func__);
 + goto out;
 + }
 +
 + response = kzalloc(sizeof(struct ufs_query_res), GFP_KERNEL);
 + if (!response) {
 + dev_err(hba-dev,
 + %s: 

Re: [PATCH V3 2/2] scsi: ufs: Add runtime PM support for UFS host controller driver

2013-07-09 Thread merez
Tested-by: Maya Erez me...@codeaurora.org

 Add runtime PM helpers to suspend/resume UFS controller at runtime.
 Enable runtime PM by default for pci and platform drivers as the
 initialized hardware can suspend if it is not used after bootup.

 Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
 ---
  drivers/scsi/ufs/ufshcd-pci.c|   65
 ++---
  drivers/scsi/ufs/ufshcd-pltfrm.c |   51 +-
  drivers/scsi/ufs/ufshcd.c|8 +
  3 files changed, 117 insertions(+), 7 deletions(-)

 diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
 index 48be39a..7bd8faa 100644
 --- a/drivers/scsi/ufs/ufshcd-pci.c
 +++ b/drivers/scsi/ufs/ufshcd-pci.c
 @@ -35,6 +35,7 @@

  #include ufshcd.h
  #include linux/pci.h
 +#include linux/pm_runtime.h

  #ifdef CONFIG_PM
  /**
 @@ -44,7 +45,7 @@
   *
   * Returns -ENOSYS
   */
 -static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 +static int ufshcd_pci_suspend(struct device *dev)
  {
   /*
* TODO:
 @@ -61,7 +62,7 @@ static int ufshcd_pci_suspend(struct pci_dev *pdev,
 pm_message_t state)
   *
   * Returns -ENOSYS
   */
 -static int ufshcd_pci_resume(struct pci_dev *pdev)
 +static int ufshcd_pci_resume(struct device *dev)
  {
   /*
* TODO:
 @@ -71,8 +72,48 @@ static int ufshcd_pci_resume(struct pci_dev *pdev)

   return -ENOSYS;
  }
 +#else
 +#define ufshcd_pci_suspend   NULL
 +#define ufshcd_pci_resumeNULL
  #endif /* CONFIG_PM */

 +#ifdef CONFIG_PM_RUNTIME
 +static int ufshcd_pci_runtime_suspend(struct device *dev)
 +{
 + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
 + struct ufs_hba *hba = pci_get_drvdata(pdev);
 +
 + if (!hba)
 + return 0;
 +
 + return ufshcd_runtime_suspend(hba);
 +}
 +static int ufshcd_pci_runtime_resume(struct device *dev)
 +{
 + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
 + struct ufs_hba *hba = pci_get_drvdata(pdev);
 +
 + if (!hba)
 + return 0;
 +
 + return ufshcd_runtime_resume(hba);
 +}
 +static int ufshcd_pci_runtime_idle(struct device *dev)
 +{
 + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
 + struct ufs_hba *hba = pci_get_drvdata(pdev);
 +
 + if (!hba)
 + return 0;
 +
 + return ufshcd_runtime_idle(hba);
 +}
 +#else /* !CONFIG_PM_RUNTIME */
 +#define ufshcd_pci_runtime_suspend   NULL
 +#define ufshcd_pci_runtime_resumeNULL
 +#define ufshcd_pci_runtime_idle  NULL
 +#endif /* CONFIG_PM_RUNTIME */
 +
  /**
   * ufshcd_pci_shutdown - main function to put the controller in reset
 state
   * @pdev: pointer to PCI device handle
 @@ -91,6 +132,9 @@ static void ufshcd_pci_remove(struct pci_dev *pdev)
  {
   struct ufs_hba *hba = pci_get_drvdata(pdev);

 + pm_runtime_forbid(pdev-dev);
 + pm_runtime_get_noresume(pdev-dev);
 +
   disable_irq(pdev-irq);
   ufshcd_remove(hba);
   pci_release_regions(pdev);
 @@ -168,6 +212,8 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct
 pci_device_id *id)
   }

   pci_set_drvdata(pdev, hba);
 + pm_runtime_put_noidle(pdev-dev);
 + pm_runtime_allow(pdev-dev);

   return 0;

 @@ -182,6 +228,14 @@ out_error:
   return err;
  }

 +static const struct dev_pm_ops ufshcd_pci_pm_ops = {
 + .suspend= ufshcd_pci_suspend,
 + .resume = ufshcd_pci_resume,
 + .runtime_suspend = ufshcd_pci_runtime_suspend,
 + .runtime_resume  = ufshcd_pci_runtime_resume,
 + .runtime_idle= ufshcd_pci_runtime_idle,
 +};
 +
  static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = {
   { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
   { } /* terminate list */
 @@ -195,10 +249,9 @@ static struct pci_driver ufshcd_pci_driver = {
   .probe = ufshcd_pci_probe,
   .remove = ufshcd_pci_remove,
   .shutdown = ufshcd_pci_shutdown,
 -#ifdef CONFIG_PM
 - .suspend = ufshcd_pci_suspend,
 - .resume = ufshcd_pci_resume,
 -#endif
 + .driver = {
 + .pm = ufshcd_pci_pm_ops
 + },
  };

  module_pci_driver(ufshcd_pci_driver);
 diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c
 b/drivers/scsi/ufs/ufshcd-pltfrm.c
 index c42db40..b1f2605 100644
 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c
 +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
 @@ -34,6 +34,7 @@
   */

  #include linux/platform_device.h
 +#include linux/pm_runtime.h

  #include ufshcd.h

 @@ -87,6 +88,43 @@ static int ufshcd_pltfrm_resume(struct device *dev)
  #define ufshcd_pltfrm_resume NULL
  #endif

 +#ifdef CONFIG_PM_RUNTIME
 +static int ufshcd_pltfrm_runtime_suspend(struct device *dev)
 +{
 + struct platform_device *pdev = to_platform_device(dev);
 + struct ufs_hba *hba =  platform_get_drvdata(pdev);
 +
 + if (!hba)
 + return 0;
 +
 + return ufshcd_runtime_suspend(hba);
 +}
 +static int ufshcd_pltfrm_runtime_resume(struct device *dev)
 +{
 + struct 

Re: [PATCH V3 2/4] scsi: ufs: Fix hardware race conditions while aborting a command

2013-07-09 Thread merez
Tested-by: Maya Erez me...@codeaurora.org

 There is a possible race condition in the hardware when the abort
 command is issued to terminate the ongoing SCSI command as described
 below:

 - A bit in the door-bell register is set in the controller for a
   new SCSI command.
 - In some rare situations, before controller get a chance to issue
   the command to the device, the software issued an abort command.
 - If the device recieves abort command first then it returns success
   because the command itself is not present.
 - Now if the controller commits the command to device it will be
   processed.
 - Software thinks that command is aborted and proceed while still
   the device is processing it.
 - The software, controller and device may go out of sync because of
   this race condition.

 To avoid this, query task presence in the device before sending abort
 task command so that after the abort operation, the command is guaranteed
 to be non-existent in both controller and the device.

 Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
 ---
  drivers/scsi/ufs/ufshcd.c |   70
 +++-
  1 files changed, 55 insertions(+), 15 deletions(-)

 diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
 index a176421..51ce096 100644
 --- a/drivers/scsi/ufs/ufshcd.c
 +++ b/drivers/scsi/ufs/ufshcd.c
 @@ -2550,6 +2550,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd)
   * ufshcd_abort - abort a specific command
   * @cmd: SCSI command pointer
   *
 + * Abort the pending command in device by sending UFS_ABORT_TASK task
 management
 + * command, and in host controller by clearing the door-bell register.
 There can
 + * be race between controller sending the command to the device while
 abort is
 + * issued. To avoid that, first issue UFS_QUERY_TASK to check if the
 command is
 + * really issued and then try to abort it.
 + *
   * Returns SUCCESS/FAILED
   */
  static int ufshcd_abort(struct scsi_cmnd *cmd)
 @@ -2558,7 +2564,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
   struct ufs_hba *hba;
   unsigned long flags;
   unsigned int tag;
 - int err;
 + int err = 0;
 + int poll_cnt;
   u8 resp;
   struct ufshcd_lrb *lrbp;

 @@ -2566,33 +2573,59 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
   hba = shost_priv(host);
   tag = cmd-request-tag;

 - spin_lock_irqsave(host-host_lock, flags);
 + /* If command is already aborted/completed, return SUCCESS */
 + if (!(test_bit(tag, hba-outstanding_reqs)))
 + goto out;

 - /* check if command is still pending */
 - if (!(test_bit(tag, hba-outstanding_reqs))) {
 - err = FAILED;
 - spin_unlock_irqrestore(host-host_lock, flags);
 + lrbp = hba-lrb[tag];
 + for (poll_cnt = 100; poll_cnt; poll_cnt--) {
 + err = ufshcd_issue_tm_cmd(hba, lrbp-lun, lrbp-task_tag,
 + UFS_QUERY_TASK, resp);
 + if (!err  resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) {
 + /* cmd pending in the device */
 + break;
 + } else if (!err  resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
 + u32 reg;
 +
 + /*
 +  * cmd not pending in the device, check if it is
 +  * in transition.
 +  */
 + reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
 + if (reg  (1  tag)) {
 + /* sleep for max. 2ms to stabilize */
 + usleep_range(1000, 2000);
 + continue;
 + }
 + /* command completed already */
 + goto out;
 + } else {
 + if (!err)
 + err = resp; /* service response error */
 + goto out;
 + }
 + }
 +
 + if (!poll_cnt) {
 + err = -EBUSY;
   goto out;
   }
 - spin_unlock_irqrestore(host-host_lock, flags);

 - lrbp = hba-lrb[tag];
   err = ufshcd_issue_tm_cmd(hba, lrbp-lun, lrbp-task_tag,
   UFS_ABORT_TASK, resp);
   if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
 - err = FAILED;
 + if (!err)
 + err = resp; /* service response error */
   goto out;
 - } else {
 - err = SUCCESS;
   }

 + err = ufshcd_clear_cmd(hba, tag);
 + if (err)
 + goto out;
 +
   scsi_dma_unmap(cmd);

   spin_lock_irqsave(host-host_lock, flags);
 -
 - /* clear the respective UTRLCLR register bit */
 - ufshcd_utrl_clear(hba, tag);
 -
   __clear_bit(tag, hba-outstanding_reqs);
   hba-lrb[tag].cmd = NULL;
   spin_unlock_irqrestore(host-host_lock, flags);
 @@ -2600,6 +2633,13 @@ static int ufshcd_abort(struct scsi_cmnd 

Re: [PATCH V3 1/4] scsi: ufs: Fix broken task management command implementation

2013-07-09 Thread merez
Tested-by: Maya Erez me...@codeaurora.org

 Currently, sending Task Management (TM) command to the card might
 be broken in some scenarios as listed below:

 Problem: If there are more than 8 TM commands the implementation
  returns error to the caller.
 Fix: Wait for one of the slots to be emptied and send the command.

 Problem: Sometimes it is necessary for the caller to know the TM service
  response code to determine the task status.
 Fix: Propogate the service response to the caller.

 Problem: If the TM command times out no proper error recovery is
  implemented.
 Fix: Clear the command in the controller door-bell register, so that
  further commands for the same slot don't fail.

 Problem: While preparing the TM command descriptor, the task tag used
  should be unique across SCSI/NOP/QUERY/TM commands and not the
task tag of the command which the TM command is trying to manage.
 Fix: Use a unique task tag instead of task tag of SCSI command.

 Problem: Since the TM command involves H/W communication, abruptly ending
  the request on kill interrupt signal might cause h/w malfunction.
 Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE
  set.

 Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
 ---
  drivers/scsi/ufs/ufshcd.c |  177
 ++---
  drivers/scsi/ufs/ufshcd.h |8 ++-
  2 files changed, 126 insertions(+), 59 deletions(-)

 diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
 index af7d01d..a176421 100644
 --- a/drivers/scsi/ufs/ufshcd.c
 +++ b/drivers/scsi/ufs/ufshcd.c
 @@ -53,6 +53,9 @@
  /* Query request timeout */
  #define QUERY_REQ_TIMEOUT 30 /* msec */

 +/* Task management command timeout */
 +#define TM_CMD_TIMEOUT   100 /* msecs */
 +
  /* Expose the flag value from utp_upiu_query.value */
  #define MASK_QUERY_UPIU_FLAG_LOC 0xFF

 @@ -190,13 +193,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc
 *task_req_descp)
  /**
   * ufshcd_get_tm_free_slot - get a free slot for task management request
   * @hba: per adapter instance
 + * @free_slot: pointer to variable with available slot value
   *
 - * Returns maximum number of task management request slots in case of
 - * task management queue full or returns the free slot number
 + * Get a free tag and lock it until ufshcd_put_tm_slot() is called.
 + * Returns 0 if free slot is not available, else return 1 with tag value
 + * in @free_slot.
   */
 -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba)
 +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot)
 +{
 + int tag;
 + bool ret = false;
 +
 + if (!free_slot)
 + goto out;
 +
 + do {
 + tag = find_first_zero_bit(hba-tm_slots_in_use, hba-nutmrs);
 + if (tag = hba-nutmrs)
 + goto out;
 + } while (test_and_set_bit_lock(tag, hba-tm_slots_in_use));
 +
 + *free_slot = tag;
 + ret = true;
 +out:
 + return ret;
 +}
 +
 +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot)
  {
 - return find_first_zero_bit(hba-outstanding_tasks, hba-nutmrs);
 + clear_bit_unlock(slot, hba-tm_slots_in_use);
  }

  /**
 @@ -1778,10 +1803,11 @@ static void ufshcd_slave_destroy(struct
 scsi_device *sdev)
   * ufshcd_task_req_compl - handle task management request completion
   * @hba: per adapter instance
   * @index: index of the completed request
 + * @resp: task management service response
   *
 - * Returns SUCCESS/FAILED
 + * Returns non-zero value on error, zero on success
   */
 -static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index)
 +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8
 *resp)
  {
   struct utp_task_req_desc *task_req_descp;
   struct utp_upiu_task_rsp *task_rsp_upiup;
 @@ -1802,19 +1828,15 @@ static int ufshcd_task_req_compl(struct ufs_hba
 *hba, u32 index)
   task_req_descp[index].task_rsp_upiu;
   task_result = be32_to_cpu(task_rsp_upiup-header.dword_1);
   task_result = ((task_result  MASK_TASK_RESPONSE)  8);
 -
 - if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL 
 - task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED)
 - task_result = FAILED;
 - else
 - task_result = SUCCESS;
 + if (resp)
 + *resp = (u8)task_result;
   } else {
 - task_result = FAILED;
 - dev_err(hba-dev,
 - trc: Invalid ocs = %x\n, ocs_value);
 + dev_err(hba-dev, %s: failed, ocs = 0x%x\n,
 + __func__, ocs_value);
   }
   spin_unlock_irqrestore(hba-host-host_lock, flags);
 - return task_result;
 +
 + return ocs_value;
  }

  /**
 @@ -2298,7 +2320,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba)

   

Re: [PATCH V3 3/4] scsi: ufs: Fix device and host reset methods

2013-07-09 Thread merez
Tested with error injection.

Tested-by: Maya Erez me...@codeaurora.org

 As of now SCSI initiated error handling is broken because,
 the reset APIs don't try to bring back the device initialized and
 ready for further transfers.

 In case of timeouts, the scsi error handler takes care of handling aborts
 and resets. Improve the error handling in such scenario by resetting the
 device and host and re-initializing them in proper manner.

 Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
 ---
  drivers/scsi/ufs/ufshcd.c |  467
 +++--
  drivers/scsi/ufs/ufshcd.h |2 +
  2 files changed, 411 insertions(+), 58 deletions(-)

 diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
 index 51ce096..b4c9910 100644
 --- a/drivers/scsi/ufs/ufshcd.c
 +++ b/drivers/scsi/ufs/ufshcd.c
 @@ -69,9 +69,15 @@ enum {

  /* UFSHCD states */
  enum {
 - UFSHCD_STATE_OPERATIONAL,
   UFSHCD_STATE_RESET,
   UFSHCD_STATE_ERROR,
 + UFSHCD_STATE_OPERATIONAL,
 +};
 +
 +/* UFSHCD error handling flags */
 +enum {
 + UFSHCD_EH_HOST_RESET_PENDING = (1  0),
 + UFSHCD_EH_DEVICE_RESET_PENDING = (1  1),
  };

  /* Interrupt configuration options */
 @@ -87,6 +93,22 @@ enum {
   INT_AGGR_CONFIG,
  };

 +#define ufshcd_set_device_reset_pending(h) \
 + (h-eh_flags |= UFSHCD_EH_DEVICE_RESET_PENDING)
 +#define ufshcd_set_host_reset_pending(h) \
 + (h-eh_flags |= UFSHCD_EH_HOST_RESET_PENDING)
 +#define ufshcd_device_reset_pending(h) \
 + (h-eh_flags  UFSHCD_EH_DEVICE_RESET_PENDING)
 +#define ufshcd_host_reset_pending(h) \
 + (h-eh_flags  UFSHCD_EH_HOST_RESET_PENDING)
 +#define ufshcd_clear_device_reset_pending(h) \
 + (h-eh_flags = ~UFSHCD_EH_DEVICE_RESET_PENDING)
 +#define ufshcd_clear_host_reset_pending(h) \
 + (h-eh_flags = ~UFSHCD_EH_HOST_RESET_PENDING)
 +
 +static void ufshcd_tmc_handler(struct ufs_hba *hba);
 +static void ufshcd_async_scan(void *data, async_cookie_t cookie);
 +
  /*
   * ufshcd_wait_for_register - wait for register value to change
   * @hba - per-adapter interface
 @@ -851,9 +873,22 @@ static int ufshcd_queuecommand(struct Scsi_Host
 *host, struct scsi_cmnd *cmd)

   tag = cmd-request-tag;

 - if (hba-ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
 + switch (hba-ufshcd_state) {
 + case UFSHCD_STATE_OPERATIONAL:
 + break;
 + case UFSHCD_STATE_RESET:
   err = SCSI_MLQUEUE_HOST_BUSY;
   goto out;
 + case UFSHCD_STATE_ERROR:
 + set_host_byte(cmd, DID_ERROR);
 + cmd-scsi_done(cmd);
 + goto out;
 + default:
 + dev_WARN_ONCE(hba-dev, 1, %s: invalid state %d\n,
 + __func__, hba-ufshcd_state);
 + set_host_byte(cmd, DID_BAD_TARGET);
 + cmd-scsi_done(cmd);
 + goto out;
   }

   /* acquire the tag to make sure device cmds don't use it */
 @@ -1573,8 +1608,6 @@ static int ufshcd_make_hba_operational(struct
 ufs_hba *hba)
   if (hba-ufshcd_state == UFSHCD_STATE_RESET)
   scsi_unblock_requests(hba-host);

 - hba-ufshcd_state = UFSHCD_STATE_OPERATIONAL;
 -
  out:
   return err;
  }
 @@ -2273,6 +2306,106 @@ out:
  }

  /**
 + * ufshcd_utrl_is_rsr_enabled - check if run-stop register is enabled
 + * @hba: per-adapter instance
 + */
 +static bool ufshcd_utrl_is_rsr_enabled(struct ufs_hba *hba)
 +{
 + return ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_LIST_RUN_STOP)  0x1;
 +}
 +
 +/**
 + * ufshcd_utmrl_is_rsr_enabled - check if run-stop register is enabled
 + * @hba: per-adapter instance
 + */
 +static bool ufshcd_utmrl_is_rsr_enabled(struct ufs_hba *hba)
 +{
 + return ufshcd_readl(hba, REG_UTP_TASK_REQ_LIST_RUN_STOP)  0x1;
 +}
 +
 +/**
 + * ufshcd_complete_pending_tasks - complete outstanding tasks
 + * @hba: per adapter instance
 + *
 + * Abort in-progress task management commands and wakeup
 + * waiting threads.
 + *
 + * Returns non-zero error value when failed to clear all the commands.
 + */
 +static int ufshcd_complete_pending_tasks(struct ufs_hba *hba)
 +{
 + u32 reg;
 + int err = 0;
 + unsigned long flags;
 +
 + if (!hba-outstanding_tasks)
 + goto out;
 +
 + /* Clear UTMRL only when run-stop is enabled */
 + if (ufshcd_utmrl_is_rsr_enabled(hba))
 + ufshcd_writel(hba, ~hba-outstanding_tasks,
 + REG_UTP_TASK_REQ_LIST_CLEAR);
 +
 + /* poll for max. 1 sec to clear door bell register by h/w */
 + reg = ufshcd_wait_for_register(hba,
 + REG_UTP_TASK_REQ_DOOR_BELL,
 + hba-outstanding_tasks, 0, 1000, 1000);
 + if (reg  hba-outstanding_tasks)
 + err = -ETIMEDOUT;
 +
 + spin_lock_irqsave(hba-host-host_lock, flags);
 + /* complete commands that were cleared out */
 + ufshcd_tmc_handler(hba);
 + spin_unlock_irqrestore(hba-host-host_lock, flags);
 +out:
 + if (err)
 +   

Re: [PATCH V3 4/4] scsi: ufs: Improve UFS fatal error handling

2013-07-09 Thread merez
Tested with error injection.

Tested-by: Maya Erez me...@codeaurora.org

 Error handling in UFS driver is broken and resets the host controller
 for fatal errors without re-initialization. Correct the fatal error
 handling sequence according to UFS Host Controller Interface (HCI)
 v1.1 specification.

 o Upon determining fatal error condition the host controller may hang
   forever until a reset is applied, so just retrying the command doesn't
   work without a reset. So, the reset is applied in the driver context
   in a separate work and SCSI mid-layer isn't informed until reset is
   applied.

 o Processed requests which are completed without error are reported to
   SCSI layer as successful and any pending commands that are not started
   yet or are not cause of the error are re-queued into scsi midlayer
 queue.
   For the command that caused error, host controller or device is reset
   and DID_ERROR is returned for command retry after applying reset.

 o SCSI is informed about the expected Unit-Attentioni exception from the
   device for the immediate command after a reset so that the SCSI layer
   take necessary steps to establish communication with the device.

 Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
 ---
  drivers/scsi/ufs/ufshcd.c |  349
 +++-
  drivers/scsi/ufs/ufshcd.h |2 +
  drivers/scsi/ufs/ufshci.h |   19 ++-
  3 files changed, 295 insertions(+), 75 deletions(-)

 diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
 index b4c9910..2a3874f 100644
 --- a/drivers/scsi/ufs/ufshcd.c
 +++ b/drivers/scsi/ufs/ufshcd.c
 @@ -80,6 +80,14 @@ enum {
   UFSHCD_EH_DEVICE_RESET_PENDING = (1  1),
  };

 +/* UFSHCD UIC layer error flags */
 +enum {
 + UFSHCD_UIC_DL_PA_INIT_ERROR = (1  0), /* Data link layer error */
 + UFSHCD_UIC_NL_ERROR = (1  1), /* Network layer error */
 + UFSHCD_UIC_TL_ERROR = (1  2), /* Transport Layer error */
 + UFSHCD_UIC_DME_ERROR = (1  3), /* DME error */
 +};
 +
  /* Interrupt configuration options */
  enum {
   UFSHCD_INT_DISABLE,
 @@ -108,6 +116,7 @@ enum {

  static void ufshcd_tmc_handler(struct ufs_hba *hba);
  static void ufshcd_async_scan(void *data, async_cookie_t cookie);
 +static int ufshcd_reset_and_restore(struct ufs_hba *hba);

  /*
   * ufshcd_wait_for_register - wait for register value to change
 @@ -1605,9 +1614,6 @@ static int ufshcd_make_hba_operational(struct
 ufs_hba *hba)
   goto out;
   }

 - if (hba-ufshcd_state == UFSHCD_STATE_RESET)
 - scsi_unblock_requests(hba-host);
 -
  out:
   return err;
  }
 @@ -1733,66 +1739,6 @@ static int ufshcd_validate_dev_connection(struct
 ufs_hba *hba)
  }

  /**
 - * ufshcd_do_reset - reset the host controller
 - * @hba: per adapter instance
 - *
 - * Returns SUCCESS/FAILED
 - */
 -static int ufshcd_do_reset(struct ufs_hba *hba)
 -{
 - struct ufshcd_lrb *lrbp;
 - unsigned long flags;
 - int tag;
 -
 - /* block commands from midlayer */
 - scsi_block_requests(hba-host);
 -
 - spin_lock_irqsave(hba-host-host_lock, flags);
 - hba-ufshcd_state = UFSHCD_STATE_RESET;
 -
 - /* send controller to reset state */
 - ufshcd_hba_stop(hba);
 - spin_unlock_irqrestore(hba-host-host_lock, flags);
 -
 - /* abort outstanding commands */
 - for (tag = 0; tag  hba-nutrs; tag++) {
 - if (test_bit(tag, hba-outstanding_reqs)) {
 - lrbp = hba-lrb[tag];
 - if (lrbp-cmd) {
 - scsi_dma_unmap(lrbp-cmd);
 - lrbp-cmd-result = DID_RESET  16;
 - lrbp-cmd-scsi_done(lrbp-cmd);
 - lrbp-cmd = NULL;
 - clear_bit_unlock(tag, hba-lrb_in_use);
 - }
 - }
 - }
 -
 - /* complete device management command */
 - if (hba-dev_cmd.complete)
 - complete(hba-dev_cmd.complete);
 -
 - /* clear outstanding request/task bit maps */
 - hba-outstanding_reqs = 0;
 - hba-outstanding_tasks = 0;
 -
 - /* 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;
 -}
 -
 -/**
   * ufshcd_slave_alloc - handle initial SCSI device configurations
   * @sdev: pointer to SCSI device
   *
 @@ -1809,6 +1755,9 @@ static int ufshcd_slave_alloc(struct scsi_device
 *sdev)
   sdev-use_10_for_ms = 1;
   scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);

 + /* allow SCSI layer to restart the device in case of errors */
 + sdev-allow_restart = 1;
 +
   /*
* Inform SCSI Midlayer that the LUN queue depth is same 

[PATCH v2 0/7] usb: phy: msm: Fixes and cleanups

2013-07-09 Thread Ivan T. Ivanov
From: Ivan T. Ivanov iiva...@mm-sol.com

Changes since first version.
* Extend commit messages a little bit.

Following patches make initial cleanup of usb phy found in the Qualcomm
chipsets. Changes include:
* Build time error fix.
* Move driver to Managed Device Resource allocation.
* Checkpatch warnings and error fixes
* Removed usage of global regulators variables.

Ivan T. Ivanov (7):
  usb: phy: msm: Move mach depndend code to platform data
  usb: phy: msm: Migrate to Managed Device Resource allocation
  usb: phy: msm: Move regulator usage to managed resource allocation
  usb: phy: msm: Remove unnecessarily check for valid regulators.
  usb: phy: msm: Fix WARNING: quoted string split across lines
  usb: phy: msm: Fix WARNING: Prefer seq_puts to seq_printf
  usb: phy: msm: Lindent the code

 arch/arm/mach-msm/board-msm7x30.c |   35 
 arch/arm/mach-msm/board-qsd8x50.c |   34 
 drivers/usb/phy/phy-msm-usb.c |  384 ++---
 include/linux/usb/msm_hsusb.h |5 +
 4 files changed, 220 insertions(+), 238 deletions(-)

-- 
1.7.9.5

--
To unsubscribe from this list: send the line unsubscribe linux-arm-msm in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v2 3/7] usb: phy: msm: Move regulator usage to managed resource allocation

2013-07-09 Thread Ivan T. Ivanov
From: Ivan T. Ivanov iiva...@mm-sol.com

This patch move global regulators variables to driver state
structire and move allocation of the regulators to be devm managed.

Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com
---
 drivers/usb/phy/phy-msm-usb.c |  111 +++--
 include/linux/usb/msm_hsusb.h |3 ++
 2 files changed, 53 insertions(+), 61 deletions(-)

diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index cc37f5e..8289270 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -58,47 +58,32 @@
 #define USB_PHY_VDD_DIG_VOL_MIN100 /* uV */
 #define USB_PHY_VDD_DIG_VOL_MAX132 /* uV */
 
-static struct regulator *hsusb_3p3;
-static struct regulator *hsusb_1p8;
-static struct regulator *hsusb_vddcx;
-
 static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init)
 {
int ret = 0;
 
if (init) {
-   hsusb_vddcx = regulator_get(motg-phy.dev, HSUSB_VDDCX);
-   if (IS_ERR(hsusb_vddcx)) {
-   dev_err(motg-phy.dev, unable to get hsusb vddcx\n);
-   return PTR_ERR(hsusb_vddcx);
-   }
-
-   ret = regulator_set_voltage(hsusb_vddcx,
+   ret = regulator_set_voltage(motg-vddcx,
USB_PHY_VDD_DIG_VOL_MIN,
USB_PHY_VDD_DIG_VOL_MAX);
if (ret) {
dev_err(motg-phy.dev, unable to set the voltage 
for hsusb vddcx\n);
-   regulator_put(hsusb_vddcx);
return ret;
}
 
-   ret = regulator_enable(hsusb_vddcx);
-   if (ret) {
+   ret = regulator_enable(motg-vddcx);
+   if (ret)
dev_err(motg-phy.dev, unable to enable hsusb 
vddcx\n);
-   regulator_put(hsusb_vddcx);
-   }
} else {
-   ret = regulator_set_voltage(hsusb_vddcx, 0,
+   ret = regulator_set_voltage(motg-vddcx, 0,
USB_PHY_VDD_DIG_VOL_MAX);
if (ret)
dev_err(motg-phy.dev, unable to set the voltage 
for hsusb vddcx\n);
-   ret = regulator_disable(hsusb_vddcx);
+   ret = regulator_disable(motg-vddcx);
if (ret)
dev_err(motg-phy.dev, unable to disable hsusb 
vddcx\n);
-
-   regulator_put(hsusb_vddcx);
}
 
return ret;
@@ -109,59 +94,44 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int 
init)
int rc = 0;
 
if (init) {
-   hsusb_3p3 = regulator_get(motg-phy.dev, HSUSB_3p3);
-   if (IS_ERR(hsusb_3p3)) {
-   dev_err(motg-phy.dev, unable to get hsusb 3p3\n);
-   return PTR_ERR(hsusb_3p3);
-   }
-
-   rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN,
+   rc = regulator_set_voltage(motg-v3p3, USB_PHY_3P3_VOL_MIN,
USB_PHY_3P3_VOL_MAX);
if (rc) {
dev_err(motg-phy.dev, unable to set voltage level 
for hsusb 3p3\n);
-   goto put_3p3;
+   return rc;
}
-   rc = regulator_enable(hsusb_3p3);
+   rc = regulator_enable(motg-v3p3);
if (rc) {
dev_err(motg-phy.dev, unable to enable the hsusb 
3p3\n);
-   goto put_3p3;
-   }
-   hsusb_1p8 = regulator_get(motg-phy.dev, HSUSB_1p8);
-   if (IS_ERR(hsusb_1p8)) {
-   dev_err(motg-phy.dev, unable to get hsusb 1p8\n);
-   rc = PTR_ERR(hsusb_1p8);
-   goto disable_3p3;
+   return rc;
}
-   rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN,
+
+   rc = regulator_set_voltage(motg-v1p8, USB_PHY_1P8_VOL_MIN,
USB_PHY_1P8_VOL_MAX);
if (rc) {
dev_err(motg-phy.dev, unable to set voltage level 
for hsusb 1p8\n);
-   goto put_1p8;
+   goto disable_3p3;
}
-   rc = regulator_enable(hsusb_1p8);
+   rc = regulator_enable(motg-v1p8);
if (rc) {
dev_err(motg-phy.dev, unable to enable the hsusb 
1p8\n);
-   goto put_1p8;
+   goto disable_3p3;
}
 
return 0;
}
 
-   regulator_disable(hsusb_1p8);
-put_1p8:
-   regulator_put(hsusb_1p8);
+   regulator_disable(motg-v1p8);
 

[PATCH v2 2/7] usb: phy: msm: Migrate to Managed Device Resource allocation

2013-07-09 Thread Ivan T. Ivanov
From: Ivan T. Ivanov iiva...@mm-sol.com

Use managed device resources to clean up the probe/remove
and get DT support for free.

Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com
---
 drivers/usb/phy/phy-msm-usb.c |   78 +++--
 1 file changed, 20 insertions(+), 58 deletions(-)

diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index ab1b880..cc37f5e 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -1397,13 +1397,14 @@ static int __init msm_otg_probe(struct platform_device 
*pdev)
return -ENODEV;
}
 
-   motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL);
+   motg = devm_kzalloc(pdev-dev, sizeof(*motg), GFP_KERNEL);
if (!motg) {
dev_err(pdev-dev, unable to allocate msm_otg\n);
return -ENOMEM;
}
 
-   motg-phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+   motg-phy.otg = devm_kzalloc(pdev-dev, sizeof(*motg-phy.otg),
+GFP_KERNEL);
if (!motg-phy.otg) {
dev_err(pdev-dev, unable to allocate msm_otg\n);
return -ENOMEM;
@@ -1413,18 +1414,16 @@ static int __init msm_otg_probe(struct platform_device 
*pdev)
phy = motg-phy;
phy-dev = pdev-dev;
 
-   motg-phy_reset_clk = clk_get(pdev-dev, usb_phy_clk);
+   motg-phy_reset_clk = devm_clk_get(pdev-dev, usb_phy_clk);
if (IS_ERR(motg-phy_reset_clk)) {
dev_err(pdev-dev, failed to get usb_phy_clk\n);
-   ret = PTR_ERR(motg-phy_reset_clk);
-   goto free_motg;
+   return PTR_ERR(motg-phy_reset_clk);
}
 
-   motg-clk = clk_get(pdev-dev, usb_hs_clk);
+   motg-clk = devm_clk_get(pdev-dev, usb_hs_clk);
if (IS_ERR(motg-clk)) {
dev_err(pdev-dev, failed to get usb_hs_clk\n);
-   ret = PTR_ERR(motg-clk);
-   goto put_phy_reset_clk;
+   return PTR_ERR(motg-clk);
}
clk_set_rate(motg-clk, 6000);
 
@@ -1436,21 +1435,19 @@ static int __init msm_otg_probe(struct platform_device 
*pdev)
 * on pclk source
 */
 if (motg-pdata-pclk_src_name) {
-   motg-pclk_src = clk_get(pdev-dev,
+   motg-pclk_src = devm_clk_get(pdev-dev,
motg-pdata-pclk_src_name);
if (IS_ERR(motg-pclk_src))
-   goto put_clk;
+   return PTR_ERR(motg-pclk_src);
clk_set_rate(motg-pclk_src, INT_MAX);
clk_prepare_enable(motg-pclk_src);
} else
motg-pclk_src = ERR_PTR(-ENOENT);
 
-
-   motg-pclk = clk_get(pdev-dev, usb_hs_pclk);
+   motg-pclk = devm_clk_get(pdev-dev, usb_hs_pclk);
if (IS_ERR(motg-pclk)) {
dev_err(pdev-dev, failed to get usb_hs_pclk\n);
-   ret = PTR_ERR(motg-pclk);
-   goto put_pclk_src;
+   return PTR_ERR(motg-pclk);
}
 
/*
@@ -1458,30 +1455,27 @@ static int __init msm_otg_probe(struct platform_device 
*pdev)
 * clock is introduced to remove the dependency on AXI
 * bus frequency.
 */
-   motg-core_clk = clk_get(pdev-dev, usb_hs_core_clk);
+   motg-core_clk = devm_clk_get(pdev-dev, usb_hs_core_clk);
if (IS_ERR(motg-core_clk))
motg-core_clk = NULL;
 
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(pdev-dev, failed to get platform resource mem\n);
-   ret = -ENODEV;
-   goto put_core_clk;
+   return -ENODEV;
}
 
-   motg-regs = ioremap(res-start, resource_size(res));
+   motg-regs = devm_ioremap_resource(pdev-dev, res);
if (!motg-regs) {
dev_err(pdev-dev, ioremap failed\n);
-   ret = -ENOMEM;
-   goto put_core_clk;
+   return PTR_ERR(motg-regs);
}
dev_info(pdev-dev, OTG regs = %p\n, motg-regs);
 
motg-irq = platform_get_irq(pdev, 0);
-   if (!motg-irq) {
+   if (motg-irq  0) {
dev_err(pdev-dev, platform_get_irq failed\n);
-   ret = -ENODEV;
-   goto free_regs;
+   return motg-irq;
}
 
clk_prepare_enable(motg-clk);
@@ -1490,7 +1484,7 @@ static int __init msm_otg_probe(struct platform_device 
*pdev)
ret = msm_hsusb_init_vddcx(motg, 1);
if (ret) {
dev_err(pdev-dev, hsusb vddcx configuration failed\n);
-   goto free_regs;
+   return ret;
}
 
ret = msm_hsusb_ldo_init(motg, 1);
@@ -1512,7 +1506,7 @@ static int __init msm_otg_probe(struct platform_device 
*pdev)
 
INIT_WORK(motg-sm_work, msm_otg_sm_work);
INIT_DELAYED_WORK(motg-chg_work, msm_chg_detect_work);
-   ret = request_irq(motg-irq, msm_otg_irq, 

[PATCH v2 7/7] usb: phy: msm: Lindent the code

2013-07-09 Thread Ivan T. Ivanov
From: Ivan T. Ivanov iiva...@mm-sol.com

Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com
---
 drivers/usb/phy/phy-msm-usb.c |   99 ++---
 1 file changed, 52 insertions(+), 47 deletions(-)

diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index 6d05085..111f454 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -45,18 +45,18 @@
 
 #define ULPI_IO_TIMEOUT_USEC   (10 * 1000)
 
-#define USB_PHY_3P3_VOL_MIN305 /* uV */
-#define USB_PHY_3P3_VOL_MAX330 /* uV */
+#define USB_PHY_3P3_VOL_MIN305 /* uV */
+#define USB_PHY_3P3_VOL_MAX330 /* uV */
 #define USB_PHY_3P3_HPM_LOAD   5   /* uA */
 #define USB_PHY_3P3_LPM_LOAD   4000/* uA */
 
-#define USB_PHY_1P8_VOL_MIN180 /* uV */
-#define USB_PHY_1P8_VOL_MAX180 /* uV */
+#define USB_PHY_1P8_VOL_MIN180 /* uV */
+#define USB_PHY_1P8_VOL_MAX180 /* uV */
 #define USB_PHY_1P8_HPM_LOAD   5   /* uA */
 #define USB_PHY_1P8_LPM_LOAD   4000/* uA */
 
-#define USB_PHY_VDD_DIG_VOL_MIN100 /* uV */
-#define USB_PHY_VDD_DIG_VOL_MAX132 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MIN100 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MAX132 /* uV */
 
 static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init)
 {
@@ -64,8 +64,8 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int 
init)
 
if (init) {
ret = regulator_set_voltage(motg-vddcx,
-   USB_PHY_VDD_DIG_VOL_MIN,
-   USB_PHY_VDD_DIG_VOL_MAX);
+   USB_PHY_VDD_DIG_VOL_MIN,
+   USB_PHY_VDD_DIG_VOL_MAX);
if (ret) {
dev_err(motg-phy.dev, Cannot set vddcx voltage\n);
return ret;
@@ -73,15 +73,17 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int 
init)
 
ret = regulator_enable(motg-vddcx);
if (ret)
-   dev_err(motg-phy.dev, unable to enable hsusb 
vddcx\n);
+   dev_err(motg-phy.dev,
+   unable to enable hsusb vddcx\n);
} else {
ret = regulator_set_voltage(motg-vddcx, 0,
-   USB_PHY_VDD_DIG_VOL_MAX);
+   USB_PHY_VDD_DIG_VOL_MAX);
if (ret)
dev_err(motg-phy.dev, Cannot set vddcx voltage\n);
ret = regulator_disable(motg-vddcx);
if (ret)
-   dev_err(motg-phy.dev, unable to disable hsusb 
vddcx\n);
+   dev_err(motg-phy.dev,
+   unable to disable hsusb vddcx\n);
}
 
return ret;
@@ -93,26 +95,28 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int 
init)
 
if (init) {
rc = regulator_set_voltage(motg-v3p3, USB_PHY_3P3_VOL_MIN,
-   USB_PHY_3P3_VOL_MAX);
+  USB_PHY_3P3_VOL_MAX);
if (rc) {
dev_err(motg-phy.dev, Cannot set v3p3 voltage\n);
return rc;
}
rc = regulator_enable(motg-v3p3);
if (rc) {
-   dev_err(motg-phy.dev, unable to enable the hsusb 
3p3\n);
+   dev_err(motg-phy.dev,
+   unable to enable the hsusb 3p3\n);
return rc;
}
 
rc = regulator_set_voltage(motg-v1p8, USB_PHY_1P8_VOL_MIN,
-   USB_PHY_1P8_VOL_MAX);
+  USB_PHY_1P8_VOL_MAX);
if (rc) {
dev_err(motg-phy.dev, Cannot set v1p8 voltage\n);
goto disable_3p3;
}
rc = regulator_enable(motg-v1p8);
if (rc) {
-   dev_err(motg-phy.dev, unable to enable the hsusb 
1p8\n);
+   dev_err(motg-phy.dev,
+   unable to enable the hsusb 1p8\n);
goto disable_3p3;
}
 
@@ -156,26 +160,26 @@ static int msm_hsusb_ldo_set_mode(struct msm_otg *motg, 
int on)
 
if (on) {
ret = regulator_set_optimum_mode(motg-v1p8,
-   USB_PHY_1P8_HPM_LOAD);
+USB_PHY_1P8_HPM_LOAD);
if (ret  0) {
pr_err(Could not set HPM for v1p8\n);
return ret;
}
ret = regulator_set_optimum_mode(motg-v3p3,
-   USB_PHY_3P3_HPM_LOAD);
+USB_PHY_3P3_HPM_LOAD);
if (ret  0) {
 

[PATCH v2 5/7] usb: phy: msm: Fix WARNING: quoted string split across lines

2013-07-09 Thread Ivan T. Ivanov
From: Ivan T. Ivanov iiva...@mm-sol.com

This fixes checkpatch.pl warnings.

Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com
---
 drivers/usb/phy/phy-msm-usb.c |   33 +++--
 1 file changed, 11 insertions(+), 22 deletions(-)

diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index 0e7d7ab..41938e6 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -67,8 +67,7 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int 
init)
USB_PHY_VDD_DIG_VOL_MIN,
USB_PHY_VDD_DIG_VOL_MAX);
if (ret) {
-   dev_err(motg-phy.dev, unable to set the voltage 
-   for hsusb vddcx\n);
+   dev_err(motg-phy.dev, Cannot set vddcx voltage\n);
return ret;
}
 
@@ -79,8 +78,7 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int 
init)
ret = regulator_set_voltage(motg-vddcx, 0,
USB_PHY_VDD_DIG_VOL_MAX);
if (ret)
-   dev_err(motg-phy.dev, unable to set the voltage 
-   for hsusb vddcx\n);
+   dev_err(motg-phy.dev, Cannot set vddcx voltage\n);
ret = regulator_disable(motg-vddcx);
if (ret)
dev_err(motg-phy.dev, unable to disable hsusb 
vddcx\n);
@@ -97,8 +95,7 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int init)
rc = regulator_set_voltage(motg-v3p3, USB_PHY_3P3_VOL_MIN,
USB_PHY_3P3_VOL_MAX);
if (rc) {
-   dev_err(motg-phy.dev, unable to set voltage level 
-   for hsusb 3p3\n);
+   dev_err(motg-phy.dev, Cannot set v3p3 voltage\n);
return rc;
}
rc = regulator_enable(motg-v3p3);
@@ -110,8 +107,7 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int 
init)
rc = regulator_set_voltage(motg-v1p8, USB_PHY_1P8_VOL_MIN,
USB_PHY_1P8_VOL_MAX);
if (rc) {
-   dev_err(motg-phy.dev, unable to set voltage level 
-   for hsusb 1p8\n);
+   dev_err(motg-phy.dev, Cannot set v1p8 voltage\n);
goto disable_3p3;
}
rc = regulator_enable(motg-v1p8);
@@ -144,8 +140,7 @@ static int msm_hsusb_config_vddcx(struct msm_otg *motg, int 
high)
 
ret = regulator_set_voltage(motg-vddcx, min_vol, max_vol);
if (ret) {
-   pr_err(%s: unable to set the voltage for regulator 
-   HSUSB_VDDCX\n, __func__);
+   dev_err(motg-phy.dev, Cannot set vddcx voltage\n);
return ret;
}
 
@@ -163,15 +158,13 @@ static int msm_hsusb_ldo_set_mode(struct msm_otg *motg, 
int on)
ret = regulator_set_optimum_mode(motg-v1p8,
USB_PHY_1P8_HPM_LOAD);
if (ret  0) {
-   pr_err(%s: Unable to set HPM of the regulator 
-   HSUSB_1p8\n, __func__);
+   pr_err(Could not set HPM for v1p8\n);
return ret;
}
ret = regulator_set_optimum_mode(motg-v3p3,
USB_PHY_3P3_HPM_LOAD);
if (ret  0) {
-   pr_err(%s: Unable to set HPM of the regulator 
-   HSUSB_3p3\n, __func__);
+   pr_err(Could not set HPM for v3p3\n);
regulator_set_optimum_mode(motg-v1p8,
USB_PHY_1P8_LPM_LOAD);
return ret;
@@ -180,13 +173,11 @@ static int msm_hsusb_ldo_set_mode(struct msm_otg *motg, 
int on)
ret = regulator_set_optimum_mode(motg-v1p8,
USB_PHY_1P8_LPM_LOAD);
if (ret  0)
-   pr_err(%s: Unable to set LPM of the regulator 
-   HSUSB_1p8\n, __func__);
+   pr_err(Could not set LPM for v1p8\n);
ret = regulator_set_optimum_mode(motg-v3p3,
USB_PHY_3P3_LPM_LOAD);
if (ret  0)
-   pr_err(%s: Unable to set LPM of the regulator 
-   HSUSB_3p3\n, __func__);
+   pr_err(Could not set LPM for v3p3\n);
}
 
pr_debug(reg (%s)\n, on ? HPM : LPM);
@@ -519,8 +510,7 @@ static int msm_otg_resume(struct msm_otg *motg)
 * PHY. USB state can not be restored. Re-insertion
 * of USB cable is the only way to get USB working.
 */

[PATCH v2 6/7] usb: phy: msm: Fix WARNING: Prefer seq_puts to seq_printf

2013-07-09 Thread Ivan T. Ivanov
From: Ivan T. Ivanov iiva...@mm-sol.com

This fixes checkpatch.pl warnings.

Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com
---
 drivers/usb/phy/phy-msm-usb.c |6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index 41938e6..6d05085 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -1204,13 +1204,13 @@ static int msm_otg_mode_show(struct seq_file *s, void 
*unused)
 
switch (otg-phy-state) {
case OTG_STATE_A_HOST:
-   seq_printf(s, host\n);
+   seq_puts(s, host\n);
break;
case OTG_STATE_B_PERIPHERAL:
-   seq_printf(s, peripheral\n);
+   seq_puts(s, peripheral\n);
break;
default:
-   seq_printf(s, none\n);
+   seq_puts(s, none\n);
break;
}
 
-- 
1.7.9.5

--
To unsubscribe from this list: send the line unsubscribe linux-arm-msm in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v2 1/7] usb: phy: msm: Move mach depndend code to platform data

2013-07-09 Thread Ivan T. Ivanov
From: Ivan T. Ivanov iiva...@mm-sol.com

This patch fix compilation error and is an intermediate step
before the addition of DeviceTree support for newer targets.
Fix suggested here: https://lkml.org/lkml/2013/6/19/381

Cc: David Brown dav...@codeaurora.org
Cc: Daniel Walker dwal...@fifo99.com
Cc: Bryan Huntsman bry...@codeaurora.org
Cc: Stephen Boyd sb...@codeaurora.org

Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com
---
 arch/arm/mach-msm/board-msm7x30.c |   35 +++
 arch/arm/mach-msm/board-qsd8x50.c |   34 +++
 drivers/usb/phy/phy-msm-usb.c |   55 ++---
 include/linux/usb/msm_hsusb.h |2 ++
 4 files changed, 85 insertions(+), 41 deletions(-)

diff --git a/arch/arm/mach-msm/board-msm7x30.c 
b/arch/arm/mach-msm/board-msm7x30.c
index db3d8c0..4df7daa 100644
--- a/arch/arm/mach-msm/board-msm7x30.c
+++ b/arch/arm/mach-msm/board-msm7x30.c
@@ -31,6 +31,7 @@
 #include asm/setup.h
 
 #include mach/board.h
+#include mach/clk.h
 #include mach/msm_iomap.h
 #include mach/dma.h
 
@@ -61,10 +62,44 @@ static int hsusb_phy_init_seq[] = {
-1
 };
 
+static int hsusb_phy_link_clk_reset(struct clk *link_clk, bool assert)
+{
+   int ret;
+
+   if (assert) {
+   ret = clk_reset(link_clk, CLK_RESET_ASSERT);
+   if (ret)
+   pr_err(usb hs_clk assert failed\n);
+   } else {
+   ret = clk_reset(link_clk, CLK_RESET_DEASSERT);
+   if (ret)
+   pr_err(usb hs_clk deassert failed\n);
+   }
+   return ret;
+}
+
+static int hsusb_phy_clk_reset(struct clk *phy_clk)
+{
+   int ret;
+
+   ret = clk_reset(phy_clk, CLK_RESET_ASSERT);
+   if (ret) {
+   pr_err(usb phy clk assert failed\n);
+   return ret;
+   }
+   usleep_range(1, 12000);
+   ret = clk_reset(phy_clk, CLK_RESET_DEASSERT);
+   if (ret)
+   pr_err(usb phy clk deassert failed\n);
+   return ret;
+}
+
 static struct msm_otg_platform_data msm_otg_pdata = {
.phy_init_seq   = hsusb_phy_init_seq,
.mode   = USB_PERIPHERAL,
.otg_control= OTG_PHY_CONTROL,
+   .phy_link_clk_reset = hsusb_phy_link_clk_reset,
+   .phy_phy_clk_reset  = hsusb_phy_clk_reset,
 };
 
 struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = {
diff --git a/arch/arm/mach-msm/board-qsd8x50.c 
b/arch/arm/mach-msm/board-qsd8x50.c
index f14a73d..d3d92ab 100644
--- a/arch/arm/mach-msm/board-qsd8x50.c
+++ b/arch/arm/mach-msm/board-qsd8x50.c
@@ -82,10 +82,44 @@ static int hsusb_phy_init_seq[] = {
-1
 };
 
+static int hsusb_phy_link_clk_reset(struct clk *link_clk, bool assert)
+{
+   int ret;
+
+   if (assert) {
+   ret = clk_reset(link_clk, CLK_RESET_ASSERT);
+   if (ret)
+   pr_err(usb hs_clk assert failed\n);
+   } else {
+   ret = clk_reset(link_clk, CLK_RESET_DEASSERT);
+   if (ret)
+   pr_err(usb hs_clk deassert failed\n);
+   }
+   return ret;
+}
+
+static int hsusb_phy_clk_reset(struct clk *phy_clk)
+{
+   int ret;
+
+   ret = clk_reset(phy_clk, CLK_RESET_ASSERT);
+   if (ret) {
+   pr_err(usb phy clk assert failed\n);
+   return ret;
+   }
+   usleep_range(1, 12000);
+   ret = clk_reset(phy_clk, CLK_RESET_DEASSERT);
+   if (ret)
+   pr_err(usb phy clk deassert failed\n);
+   return ret;
+}
+
 static struct msm_otg_platform_data msm_otg_pdata = {
.phy_init_seq   = hsusb_phy_init_seq,
.mode   = USB_PERIPHERAL,
.otg_control= OTG_PHY_CONTROL,
+   .phy_link_clk_reset = hsusb_phy_link_clk_reset,
+   .phy_phy_clk_reset  = hsusb_phy_clk_reset,
 };
 
 static struct platform_device *devices[] __initdata = {
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index d08f334..ab1b880 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -40,8 +40,6 @@
 #include linux/usb/msm_hsusb_hw.h
 #include linux/regulator/consumer.h
 
-#include mach/clk.h
-
 #define MSM_USB_BASE   (motg-regs)
 #define DRIVER_NAMEmsm_otg
 
@@ -306,51 +304,20 @@ static void ulpi_init(struct msm_otg *motg)
}
 }
 
-static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert)
-{
-   int ret;
-
-   if (assert) {
-   ret = clk_reset(motg-clk, CLK_RESET_ASSERT);
-   if (ret)
-   dev_err(motg-phy.dev, usb hs_clk assert failed\n);
-   } else {
-   ret = clk_reset(motg-clk, CLK_RESET_DEASSERT);
-   if (ret)
-   dev_err(motg-phy.dev, usb hs_clk deassert failed\n);
-   }
-   return ret;
-}
-
-static int msm_otg_phy_clk_reset(struct msm_otg *motg)
-{
-   int ret;
-
-   ret =