[PATCH 1/5] mmc: core: Add support to read command queue parameters

2014-12-02 Thread Sujit Reddy Thumma
eMMC cards with EXT_CSD version >= 7, optionally support command
queuing feature as defined by Jedec eMMC5.1. Add support for probing
command queue feature for such type of cards.

Signed-off-by: Sujit Reddy Thumma 
Signed-off-by: Asutosh Das 
---
 drivers/mmc/core/mmc.c   | 15 +++
 include/linux/mmc/card.h |  7 +++
 include/linux/mmc/mmc.h  |  6 ++
 3 files changed, 28 insertions(+)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 1ab5f3a..0ef3af5 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -571,6 +571,21 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 
*ext_csd)
card->ext_csd.data_sector_size = 512;
}
 
+   if (card->ext_csd.rev >= 7) {
+   card->ext_csd.cmdq_support = ext_csd[EXT_CSD_CMDQ_SUPPORT];
+   if (card->ext_csd.cmdq_support)
+   card->ext_csd.cmdq_depth = ext_csd[EXT_CSD_CMDQ_DEPTH];
+   }
+
+   if (card->ext_csd.rev >= 7) {
+   card->ext_csd.cmdq_support = ext_csd[EXT_CSD_CMDQ_SUPPORT];
+   if (card->ext_csd.cmdq_support)
+   card->ext_csd.cmdq_depth = ext_csd[EXT_CSD_CMDQ_DEPTH];
+   } else {
+   card->ext_csd.cmdq_support = 0;
+   card->ext_csd.cmdq_depth = 0;
+   }
+
 out:
return err;
 }
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index b730272..b87a27c 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -112,6 +112,9 @@ struct mmc_ext_csd {
u8  raw_pwr_cl_ddr_52_360;  /* 239 */
u8  raw_bkops_status;   /* 246 */
u8  raw_sectors[4]; /* 212 - 4 bytes */
+   u8  cmdq_mode_en;   /* 15 */
+   u8  cmdq_depth; /* 307 */
+   u8  cmdq_support;   /* 308 */
 
unsigned intfeature_support;
 #define MMC_DISCARD_FEATUREBIT(0)  /* CMD38 feature */
@@ -259,6 +262,7 @@ struct mmc_card {
 #define MMC_STATE_HIGHSPEED_200(1<<8)  /* card is in HS200 
mode */
 #define MMC_STATE_DOING_BKOPS  (1<<10) /* card is doing BKOPS */
 #define MMC_STATE_SUSPENDED(1<<11) /* card is suspended */
+#define MMC_STATE_CMDQ   (1<<12) /* card is in cmd queue mode */
unsigned intquirks; /* card quirks */
 #define MMC_QUIRK_LENIENT_FN0  (1<<0)  /* allow SDIO FN0 writes 
outside of the VS CCCR range */
 #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)   /* use func->cur_blksize */
@@ -427,6 +431,7 @@ static inline void __maybe_unused remove_quirk(struct 
mmc_card *card, int data)
 #define mmc_card_removed(c)((c) && ((c)->state & MMC_CARD_REMOVED))
 #define mmc_card_doing_bkops(c)((c)->state & MMC_STATE_DOING_BKOPS)
 #define mmc_card_suspended(c)  ((c)->state & MMC_STATE_SUSPENDED)
+#define mmc_card_cmdq(c)   ((c)->state & MMC_STATE_CMDQ)
 
 #define mmc_card_set_present(c)((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
@@ -441,6 +446,8 @@ static inline void __maybe_unused remove_quirk(struct 
mmc_card *card, int data)
 #define mmc_card_clr_doing_bkops(c)((c)->state &= ~MMC_STATE_DOING_BKOPS)
 #define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED)
 #define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED)
+#define mmc_card_set_cmdq(c)   ((c)->state |= MMC_STATE_CMDQ)
+#define mmc_card_clr_cmdq(c)   ((c)->state &= ~MMC_STATE_CMDQ)
 
 /*
  * Quirk add/remove for MMC products.
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 50bcde3..a893c84 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -84,6 +84,9 @@
 #define MMC_APP_CMD  55   /* ac   [31:16] RCAR1  */
 #define MMC_GEN_CMD  56   /* adtc [0] RD/WR  R1  */
 
+/* MMC 5.1 - class 11: Command Queueing */
+#define MMC_CMDQ_TASK_MGMT48  /* ac   [31:0] task ID R1b */
+
 static inline bool mmc_op_multi(u32 opcode)
 {
return opcode == MMC_WRITE_MULTIPLE_BLOCK ||
@@ -272,6 +275,7 @@ struct _mmc_csd {
  * EXT_CSD fields
  */
 
+#define EXT_CSD_CMDQ_MODE  15  /* R/W */
 #define EXT_CSD_FLUSH_CACHE32  /* W */
 #define EXT_CSD_CACHE_CTRL 33  /* R/W */
 #define EXT_CSD_POWER_OFF_NOTIFICATION 34  /* R/W */
@@ -325,6 +329,8 @@ struct _mmc_csd {
 #define EXT_CSD_POWER_OFF_LONG_TIME247 /* RO */
 #define EXT_CSD_GENERIC_CMD6_TIME  248 /* RO */
 #define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
+#define EXT_CSD_CMDQ_DEPTH 307 /* RO */
+#define EXT_CSD_CMDQ_SUPPORT

Re: [PATCH V7 1/6] scsi: ufs: fix endianness sparse warnings

2014-05-25 Thread Sujit Reddy Thumma

On 11/18/2013 2:34 PM, vinayak holikatti wrote:

On Mon, Sep 23, 2013 at 5:44 PM, vinayak holikatti
 wrote:

On Thu, Sep 19, 2013 at 4:44 PM, Sujit Reddy Thumma
 wrote:


Fix many warnings with incorrect endian assumptions
which makes the code unportable to new architectures.

The UFS specification defines the byte order as big-endian
for UPIU structure and little-endian for the host controller
transfer/task management descriptors.

Signed-off-by: Sujit Reddy Thumma 
---
  drivers/scsi/ufs/ufs.h| 36 ++--
  drivers/scsi/ufs/ufshcd.c | 42 --
  drivers/scsi/ufs/ufshci.h | 32 
  3 files changed, 42 insertions(+), 68 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 7210500..f42d1ce 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -196,9 +196,9 @@ enum {
   * @dword_2: UPIU header DW-2
   */
  struct utp_upiu_header {
-   u32 dword_0;
-   u32 dword_1;
-   u32 dword_2;
+   __be32 dword_0;
+   __be32 dword_1;
+   __be32 dword_2;
  };

  /**
@@ -207,7 +207,7 @@ struct utp_upiu_header {
   * @cdb: Command Descriptor Block CDB DW-4 to DW-7
   */
  struct utp_upiu_cmd {
-   u32 exp_data_transfer_len;
+   __be32 exp_data_transfer_len;
 u8 cdb[MAX_CDB_SIZE];
  };

@@ -228,10 +228,10 @@ struct utp_upiu_query {
 u8 idn;
 u8 index;
 u8 selector;
-   u16 reserved_osf;
-   u16 length;
-   u32 value;
-   u32 reserved[2];
+   __be16 reserved_osf;
+   __be16 length;
+   __be32 value;
+   __be32 reserved[2];
  };

  /**
@@ -256,9 +256,9 @@ struct utp_upiu_req {
   * @sense_data: Sense data field DW-8 to DW-12
   */
  struct utp_cmd_rsp {
-   u32 residual_transfer_count;
-   u32 reserved[4];
-   u16 sense_data_len;
+   __be32 residual_transfer_count;
+   __be32 reserved[4];
+   __be16 sense_data_len;
 u8 sense_data[18];
  };

@@ -286,10 +286,10 @@ struct utp_upiu_rsp {
   */
  struct utp_upiu_task_req {
 struct utp_upiu_header header;
-   u32 input_param1;
-   u32 input_param2;
-   u32 input_param3;
-   u32 reserved[2];
+   __be32 input_param1;
+   __be32 input_param2;
+   __be32 input_param3;
+   __be32 reserved[2];
  };

  /**
@@ -301,9 +301,9 @@ struct utp_upiu_task_req {
   */
  struct utp_upiu_task_rsp {
 struct utp_upiu_header header;
-   u32 output_param1;
-   u32 output_param2;
-   u32 reserved[3];
+   __be32 output_param1;
+   __be32 output_param2;
+   __be32 reserved[3];
  };

  /**
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 04884d6..064c9d9 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -163,7 +163,7 @@ static inline int ufshcd_is_device_present(u32 reg_hcs)
   */
  static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
  {
-   return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS;
+   return le32_to_cpu(lrbp->utr_descriptor_ptr->header.dword_2) & MASK_OCS;
  }

  /**
@@ -176,7 +176,7 @@ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
  static inline int
  ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp)
  {
-   return task_req_descp->header.dword_2 & MASK_OCS;
+   return le32_to_cpu(task_req_descp->header.dword_2) & MASK_OCS;
  }

  /**
@@ -390,26 +390,6 @@ static inline void ufshcd_copy_sense_data(struct 
ufshcd_lrb *lrbp)
  }

  /**
- * ufshcd_query_to_cpu() - formats the buffer to native cpu endian
- * @response: upiu query response to convert
- */
-static inline void ufshcd_query_to_cpu(struct utp_upiu_query *response)
-{
-   response->length = be16_to_cpu(response->length);
-   response->value = be32_to_cpu(response->value);
-}
-
-/**
- * ufshcd_query_to_be() - formats the buffer to big endian
- * @request: upiu query request to convert
- */
-static inline void ufshcd_query_to_be(struct utp_upiu_query *request)
-{
-   request->length = cpu_to_be16(request->length);
-   request->value = cpu_to_be32(request->value);
-}
-
-/**
   * ufshcd_copy_query_response() - Copy the Query Response and the data
   * descriptor
   * @hba: per adapter instance
@@ -425,7 +405,6 @@ void ufshcd_copy_query_response(struct ufs_hba *hba, struct 
ufshcd_lrb *lrbp)
 UPIU_RSP_CODE_OFFSET;

 memcpy(&query_res->upiu_res, &lrbp->ucd_rsp_ptr->qr, QUERY_OSF_SIZE);
-   ufshcd_query_to_cpu(&query_res->upiu_res);


 /* Get the descriptor */
@@ -749,7 +728,7 @@ static void ufshcd_prepare_utp_query_req_upiu(struct 
ufs_hba *hba,
  {
 struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
 struct ufs_query *query = &hba->dev_cmd.query;
-   u16 len = query->request.upiu_req.length;
+   u16 

[RESEND PATCH V7 1/6] scsi: ufs: fix endianness sparse warnings

2014-05-25 Thread Sujit Reddy Thumma
Fix many warnings with incorrect endian assumptions
which makes the code unportable to new architectures.

The UFS specification defines the byte order as big-endian
for UPIU structure and little-endian for the host controller
transfer/task management descriptors.

Signed-off-by: Sujit Reddy Thumma 
Acked-by: Vinayak Holikatti 
---
 drivers/scsi/ufs/ufs.h| 36 ++--
 drivers/scsi/ufs/ufshcd.c | 42 --
 drivers/scsi/ufs/ufshci.h | 32 
 3 files changed, 42 insertions(+), 68 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 7210500..f42d1ce 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -196,9 +196,9 @@ enum {
  * @dword_2: UPIU header DW-2
  */
 struct utp_upiu_header {
-   u32 dword_0;
-   u32 dword_1;
-   u32 dword_2;
+   __be32 dword_0;
+   __be32 dword_1;
+   __be32 dword_2;
 };
 
 /**
@@ -207,7 +207,7 @@ struct utp_upiu_header {
  * @cdb: Command Descriptor Block CDB DW-4 to DW-7
  */
 struct utp_upiu_cmd {
-   u32 exp_data_transfer_len;
+   __be32 exp_data_transfer_len;
u8 cdb[MAX_CDB_SIZE];
 };
 
@@ -228,10 +228,10 @@ struct utp_upiu_query {
u8 idn;
u8 index;
u8 selector;
-   u16 reserved_osf;
-   u16 length;
-   u32 value;
-   u32 reserved[2];
+   __be16 reserved_osf;
+   __be16 length;
+   __be32 value;
+   __be32 reserved[2];
 };
 
 /**
@@ -256,9 +256,9 @@ struct utp_upiu_req {
  * @sense_data: Sense data field DW-8 to DW-12
  */
 struct utp_cmd_rsp {
-   u32 residual_transfer_count;
-   u32 reserved[4];
-   u16 sense_data_len;
+   __be32 residual_transfer_count;
+   __be32 reserved[4];
+   __be16 sense_data_len;
u8 sense_data[18];
 };
 
@@ -286,10 +286,10 @@ struct utp_upiu_rsp {
  */
 struct utp_upiu_task_req {
struct utp_upiu_header header;
-   u32 input_param1;
-   u32 input_param2;
-   u32 input_param3;
-   u32 reserved[2];
+   __be32 input_param1;
+   __be32 input_param2;
+   __be32 input_param3;
+   __be32 reserved[2];
 };
 
 /**
@@ -301,9 +301,9 @@ struct utp_upiu_task_req {
  */
 struct utp_upiu_task_rsp {
struct utp_upiu_header header;
-   u32 output_param1;
-   u32 output_param2;
-   u32 reserved[3];
+   __be32 output_param1;
+   __be32 output_param2;
+   __be32 reserved[3];
 };
 
 /**
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 04884d6..064c9d9 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -163,7 +163,7 @@ static inline int ufshcd_is_device_present(u32 reg_hcs)
  */
 static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
 {
-   return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS;
+   return le32_to_cpu(lrbp->utr_descriptor_ptr->header.dword_2) & MASK_OCS;
 }
 
 /**
@@ -176,7 +176,7 @@ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
 static inline int
 ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp)
 {
-   return task_req_descp->header.dword_2 & MASK_OCS;
+   return le32_to_cpu(task_req_descp->header.dword_2) & MASK_OCS;
 }
 
 /**
@@ -390,26 +390,6 @@ static inline void ufshcd_copy_sense_data(struct 
ufshcd_lrb *lrbp)
 }
 
 /**
- * ufshcd_query_to_cpu() - formats the buffer to native cpu endian
- * @response: upiu query response to convert
- */
-static inline void ufshcd_query_to_cpu(struct utp_upiu_query *response)
-{
-   response->length = be16_to_cpu(response->length);
-   response->value = be32_to_cpu(response->value);
-}
-
-/**
- * ufshcd_query_to_be() - formats the buffer to big endian
- * @request: upiu query request to convert
- */
-static inline void ufshcd_query_to_be(struct utp_upiu_query *request)
-{
-   request->length = cpu_to_be16(request->length);
-   request->value = cpu_to_be32(request->value);
-}
-
-/**
  * ufshcd_copy_query_response() - Copy the Query Response and the data
  * descriptor
  * @hba: per adapter instance
@@ -425,7 +405,6 @@ void ufshcd_copy_query_response(struct ufs_hba *hba, struct 
ufshcd_lrb *lrbp)
UPIU_RSP_CODE_OFFSET;
 
memcpy(&query_res->upiu_res, &lrbp->ucd_rsp_ptr->qr, QUERY_OSF_SIZE);
-   ufshcd_query_to_cpu(&query_res->upiu_res);
 
 
/* Get the descriptor */
@@ -749,7 +728,7 @@ static void ufshcd_prepare_utp_query_req_upiu(struct 
ufs_hba *hba,
 {
struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
struct ufs_query *query = &hba->dev_cmd.query;
-   u16 len = query->request.upiu_req.length;
+   u16 len = be16_to_cpu(query->request.upiu_req.length);
u8 *descp = (u8 *)lrbp->ucd_req_ptr + GENERAL_UPIU_REQUEST_SIZE;
 
/* Query request header */
@@ -766,7 +745,6 @@ static void u

[RESEND PATCH V7 0/6] scsi: ufs: TM, fatal-error handling and other fixes

2014-05-25 Thread Sujit Reddy Thumma
This patch version fixes various static checker warnings along with fixing
usage of deprecated flush_work_sync() API that led James drop the patchset
from scsi-misc tree.

The first patch fixes many issues with current task management handling
in UFSHCD driver. Others improve error handling in various scenarios.

These patches are rebased on scsi-misc and are ack'ed by UFS maintainer.

Changes from v6:
- Two new patches to fix static checker warnings
- remove usage of deprecated flush_work_sync() API.
Changes from v5:
- correct typo in [PATCH V5 1/4] scsi: ufs: Fix broken task ...
- correct poll wait in [PATCH V5 2/4] scsi: ufs: Fix hardware ...
- Included Tested-by and Reviewed-by ack's.
Changes from v4:
- Addressed comments from Seungwon Jeon on V3
- Updated with proper locking while ufshcd state changes
- Retained LUN reset instead of device reset with DME_END_POINT_RESET
- Removed error handling decisions dependent on OCS value
- Simplified fatal error handling to abort the requests first
  and then carry out reset.
Changes from v3:
- Rebased.
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 (6):
  scsi: ufs: fix endianness sparse warnings
  scsi: ufs: make undeclared functions static
  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/ufs.h|  36 +--
 drivers/scsi/ufs/ufshcd.c | 722 +++---
 drivers/scsi/ufs/ufshcd.h |  22 +-
 drivers/scsi/ufs/ufshci.h |  32 +-
 4 files changed, 545 insertions(+), 267 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


[RESEND PATCH V7 3/6] scsi: ufs: Fix broken task management command implementation

2014-05-25 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
Acked-by: Vinayak Holikatti 
---
 drivers/scsi/ufs/ufshcd.c | 169 +++---
 drivers/scsi/ufs/ufshcd.h |   8 ++-
 2 files changed, 122 insertions(+), 55 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index d476cc3..c3acadc 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -55,6 +55,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
 
@@ -182,13 +185,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)
 {
-   return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs);
+   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)
+{
+   clear_bit_unlock(slot, &hba->tm_slots_in_use);
 }
 
 /**
@@ -1912,10 +1937,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;
@@ -1936,19 +1962,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;
 }
 
 /

[RESEND PATCH V7 6/6] scsi: ufs: Improve UFS fatal error handling

2014-05-25 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 Processed requests which are completed w/wo error are reported to
  SCSI layer and any pending commands that are not started are aborted
  in the controller and re-queued into scsi mid-layer queue.

o Upon determining fatal error condition the host controller may hang
  forever until a reset is applied. Block SCSI layer for sending new
  requests and apply reset in a separate error handling work.

o SCSI is informed about the expected Unit-Attention 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
Acked-by: Vinayak Holikatti 
---
 drivers/scsi/ufs/ufshcd.c | 229 --
 drivers/scsi/ufs/ufshcd.h |  10 +-
 2 files changed, 149 insertions(+), 90 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 5462310..0c28772 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -84,6 +84,14 @@ enum {
UFSHCD_EH_IN_PROGRESS = (1 << 0),
 };
 
+/* 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,
@@ -100,6 +108,8 @@ 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);
+static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag);
 
 /*
  * ufshcd_wait_for_register - wait for register value to change
@@ -1735,9 +1745,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;
 }
@@ -1863,66 +1870,6 @@ static int ufshcd_verify_dev_init(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
  *
@@ -1939,6 +1886,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 les

[RESEND PATCH V7 5/6] scsi: ufs: Fix device and host reset methods

2014-05-25 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
Acked-by: Vinayak Holikatti 
---
 drivers/scsi/ufs/ufshcd.c | 240 --
 drivers/scsi/ufs/ufshcd.h |   2 +
 2 files changed, 189 insertions(+), 53 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 52f66e4..5462310 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -74,9 +74,14 @@ enum {
 
 /* UFSHCD states */
 enum {
-   UFSHCD_STATE_OPERATIONAL,
UFSHCD_STATE_RESET,
UFSHCD_STATE_ERROR,
+   UFSHCD_STATE_OPERATIONAL,
+};
+
+/* UFSHCD error handling flags */
+enum {
+   UFSHCD_EH_IN_PROGRESS = (1 << 0),
 };
 
 /* Interrupt configuration options */
@@ -86,6 +91,16 @@ enum {
UFSHCD_INT_CLEAR,
 };
 
+#define ufshcd_set_eh_in_progress(h) \
+   (h->eh_flags |= UFSHCD_EH_IN_PROGRESS)
+#define ufshcd_eh_in_progress(h) \
+   (h->eh_flags & UFSHCD_EH_IN_PROGRESS)
+#define ufshcd_clear_eh_in_progress(h) \
+   (h->eh_flags &= ~UFSHCD_EH_IN_PROGRESS)
+
+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
@@ -856,10 +871,25 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *cmd)
 
tag = cmd->request->tag;
 
-   if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   switch (hba->ufshcd_state) {
+   case UFSHCD_STATE_OPERATIONAL:
+   break;
+   case UFSHCD_STATE_RESET:
err = SCSI_MLQUEUE_HOST_BUSY;
-   goto out;
+   goto out_unlock;
+   case UFSHCD_STATE_ERROR:
+   set_host_byte(cmd, DID_ERROR);
+   cmd->scsi_done(cmd);
+   goto out_unlock;
+   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_unlock;
}
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
 
/* acquire the tag to make sure device cmds don't use it */
if (test_and_set_bit_lock(tag, &hba->lrb_in_use)) {
@@ -896,6 +926,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *cmd)
/* issue command to the controller */
spin_lock_irqsave(hba->host->host_lock, flags);
ufshcd_send_command(hba, tag);
+out_unlock:
spin_unlock_irqrestore(hba->host->host_lock, flags);
 out:
return err;
@@ -1707,8 +1738,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;
 }
@@ -2455,8 +2484,12 @@ static void ufshcd_err_handler(struct ufs_hba *hba)
}
return;
 fatal_eh:
-   hba->ufshcd_state = UFSHCD_STATE_ERROR;
-   schedule_work(&hba->feh_workq);
+   /* handle fatal errors only when link is functional */
+   if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) {
+   /* block commands at driver layer until error is handled */
+   hba->ufshcd_state = UFSHCD_STATE_ERROR;
+   schedule_work(&hba->feh_workq);
+   }
 }
 
 /**
@@ -2621,12 +2654,13 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int 
lun_id, int task_id,
 }
 
 /**
- * ufshcd_device_reset - reset device and abort all the pending commands
+ * ufshcd_eh_device_reset_handler - device reset handler registered to
+ *scsi layer.
  * @cmd: SCSI command pointer
  *
  * Returns SUCCESS/FAILED
  */
-static int ufshcd_device_reset(struct scsi_cmnd *cmd)
+static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
 {
struct Scsi_Host *host;
struct ufs_hba *hba;
@@ -2635,6 +2669,7 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd)
int err;
u8 resp = 0xF;
struct ufshcd_lrb *lrbp;
+   unsigned long flags;
 
host = cmd->device->host;
hba = shost_priv(host);
@@ -2643,55 +2678,33 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd)
lrbp = &hba->lrb[tag];
err = ufsh

[RESEND PATCH V7 4/6] scsi: ufs: Fix hardware race conditions while aborting a command

2014-05-25 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
Acked-by: Vinayak Holikatti 
---
 drivers/scsi/ufs/ufshcd.c | 70 +--
 1 file changed, 55 insertions(+), 15 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c3acadc..52f66e4 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2695,6 +2695,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)
@@ -2703,7 +2709,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 = 0xF;
struct ufshcd_lrb *lrbp;
 
@@ -2711,33 +2718,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. 200us to stabilize */
+   usleep_range(100, 200);
+   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, &a

[RESEND PATCH V7 2/6] scsi: ufs: make undeclared functions static

2014-05-25 Thread Sujit Reddy Thumma
Make undeclared functions static and declare exported symbols
to suppress warnings from sparse tool.

Signed-off-by: Sujit Reddy Thumma 
Acked-by: Vinayak Holikatti 
---
 drivers/scsi/ufs/ufshcd.c | 4 ++--
 drivers/scsi/ufs/ufshcd.h | 2 ++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 064c9d9..d476cc3 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1148,7 +1148,7 @@ out_unlock:
  *
  * Returns 0 for success, non-zero in case of failure
 */
-int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
+static int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
enum attr_idn idn, u8 index, u8 selector, u32 *attr_val)
 {
struct ufs_query_req *request;
@@ -1459,7 +1459,7 @@ EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
  *
  * Returns 0 on success, non-zero value on failure
  */
-int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
+static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
 {
struct uic_command uic_cmd = {0};
struct completion pwr_done;
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 577679a..767ee9e 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -263,6 +263,8 @@ static inline void check_upiu_size(void)
GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE);
 }
 
+extern int ufshcd_suspend(struct ufs_hba *hba, pm_message_t state);
+extern int ufshcd_resume(struct ufs_hba *hba);
 extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
 extern int ufshcd_runtime_resume(struct ufs_hba *hba);
 extern int ufshcd_runtime_idle(struct ufs_hba *hba);
-- 
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


Re: [PATCH V2 1/3] scsi: ufs: Allow vendor specific initialization

2013-09-19 Thread Sujit Reddy Thumma
On 9/9/2013 5:03 PM, Seungwon Jeon wrote:
> Hi Sujit,
> 
> On Tue, August 27, 2013, Sujit Reddy Thumma wrote:
>> Some vendor specific controller versions might need to configure
>> vendor specific - registers, clocks, voltage regulators etc. to
>> initialize the host controller UTP layer and Uni-Pro stack.
>> Provide some common initialization operations that can be used
>> to configure vendor specifics. The methods can be extended in
>> future, for example, for power mode transitions.
>>
>> The operations are vendor/board specific and hence determined with
>> the help of compatible property in device tree.
>>
>> Signed-off-by: Sujit Reddy Thumma 
>> ---
>>   drivers/scsi/ufs/ufshcd-pci.c|   8 +-
>>   drivers/scsi/ufs/ufshcd-pltfrm.c |  24 +-
>>   drivers/scsi/ufs/ufshcd.c| 157 
>> +++
>>   drivers/scsi/ufs/ufshcd.h|  34 -
>>   4 files changed, 187 insertions(+), 36 deletions(-)
>>
>> diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
>> index a823cf4..6b0d299 100644
>> --- a/drivers/scsi/ufs/ufshcd-pci.c
>> +++ b/drivers/scsi/ufs/ufshcd-pci.c
>> @@ -191,7 +191,13 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct 
>> pci_device_id *id)
>>  return err;
>>  }
>>
>> -err = ufshcd_init(&pdev->dev, &hba, mmio_base, pdev->irq);
>> +err = ufshcd_alloc_host(&pdev->dev, &hba);
>> +if (err) {
>> +dev_err(&pdev->dev, "Allocation failed\n");
>> +return err;
>> +}
>> +
>> +err = ufshcd_init(hba, mmio_base, pdev->irq);
>>  if (err) {
>>  dev_err(&pdev->dev, "Initialization failed\n");
>>  return err;
>> diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c 
>> b/drivers/scsi/ufs/ufshcd-pltfrm.c
>> index 5e46232..9c94052 100644
>> --- a/drivers/scsi/ufs/ufshcd-pltfrm.c
>> +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
>> @@ -35,9 +35,23 @@
>>
>>   #include 
>>   #include 
>> +#include 
>>
>>   #include "ufshcd.h"
>>
>> +static const struct of_device_id ufs_of_match[];
>> +static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
>> +{
>> +if (dev->of_node) {
>> +const struct of_device_id *match;
>> +match = of_match_node(ufs_of_match, dev->of_node);
>> +if (match)
>> +return (struct ufs_hba_variant_ops *)match->data;
>> +}
>> +
>> +return NULL;
>> +}
>> +
>>   #ifdef CONFIG_PM
>>   /**
>>* ufshcd_pltfrm_suspend - suspend power management function
>> @@ -150,10 +164,18 @@ static int ufshcd_pltfrm_probe(struct platform_device 
>> *pdev)
>>  goto out;
>>  }
>>
>> +err = ufshcd_alloc_host(dev, &hba);
>> +if (err) {
>> +dev_err(&pdev->dev, "Allocation failed\n");
>> +goto out;
>> +}
>> +
>> +hba->vops = get_variant_ops(&pdev->dev);
>> +
>>  pm_runtime_set_active(&pdev->dev);
>>  pm_runtime_enable(&pdev->dev);
>>
>> -err = ufshcd_init(dev, &hba, mmio_base, irq);
>> +err = ufshcd_init(hba, mmio_base, irq);
>>  if (err) {
>>  dev_err(dev, "Intialization failed\n");
>>  goto out_disable_rpm;
>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
>> index a0f5ac2..743696a 100644
>> --- a/drivers/scsi/ufs/ufshcd.c
>> +++ b/drivers/scsi/ufs/ufshcd.c
>> @@ -174,13 +174,14 @@ static inline u32 ufshcd_get_ufs_version(struct 
>> ufs_hba *hba)
>>   /**
>>* ufshcd_is_device_present - Check if any device connected to
>>*   the host controller
>> - * @reg_hcs - host controller status register value
>> + * @hba: pointer to adapter instance
>>*
>>* Returns 1 if device present, 0 if no device detected
>>*/
>> -static inline int ufshcd_is_device_present(u32 reg_hcs)
>> +static inline int ufshcd_is_device_present(struct ufs_hba *hba)
>>   {
>> -return (DEVICE_PRESENT & reg_hcs) ? 1 : 0;
>> +return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) &
>> +DEVICE_PRESENT) ? 1 : 0;
>>   }
>>
>>   /**
>> @@ -1483,11 +1484,10 @@ out:
>>* @hba: per adapter i

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

2013-09-19 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
---
 drivers/scsi/ufs/ufshcd.c | 70 +--
 1 file changed, 55 insertions(+), 15 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c3acadc..52f66e4 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2695,6 +2695,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)
@@ -2703,7 +2709,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 = 0xF;
struct ufshcd_lrb *lrbp;
 
@@ -2711,33 +2718,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. 200us to stabilize */
+   usleep_range(100, 200);
+   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);
 

[PATCH V7 6/6] scsi: ufs: Improve UFS fatal error handling

2013-09-19 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 Processed requests which are completed w/wo error are reported to
  SCSI layer and any pending commands that are not started are aborted
  in the controller and re-queued into scsi mid-layer queue.

o Upon determining fatal error condition the host controller may hang
  forever until a reset is applied. Block SCSI layer for sending new
  requests and apply reset in a separate error handling work.

o SCSI is informed about the expected Unit-Attention 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
---
 drivers/scsi/ufs/ufshcd.c | 229 --
 drivers/scsi/ufs/ufshcd.h |  10 +-
 2 files changed, 149 insertions(+), 90 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 5462310..0c28772 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -84,6 +84,14 @@ enum {
UFSHCD_EH_IN_PROGRESS = (1 << 0),
 };
 
+/* 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,
@@ -100,6 +108,8 @@ 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);
+static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag);
 
 /*
  * ufshcd_wait_for_register - wait for register value to change
@@ -1735,9 +1745,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;
 }
@@ -1863,66 +1870,6 @@ static int ufshcd_verify_dev_init(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
  *
@@ -1939,6 +1886,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
@@ -2134

[PATCH V7 5/6] scsi: ufs: Fix device and host reset methods

2013-09-19 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
---
 drivers/scsi/ufs/ufshcd.c | 240 --
 drivers/scsi/ufs/ufshcd.h |   2 +
 2 files changed, 189 insertions(+), 53 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 52f66e4..5462310 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -74,9 +74,14 @@ enum {
 
 /* UFSHCD states */
 enum {
-   UFSHCD_STATE_OPERATIONAL,
UFSHCD_STATE_RESET,
UFSHCD_STATE_ERROR,
+   UFSHCD_STATE_OPERATIONAL,
+};
+
+/* UFSHCD error handling flags */
+enum {
+   UFSHCD_EH_IN_PROGRESS = (1 << 0),
 };
 
 /* Interrupt configuration options */
@@ -86,6 +91,16 @@ enum {
UFSHCD_INT_CLEAR,
 };
 
+#define ufshcd_set_eh_in_progress(h) \
+   (h->eh_flags |= UFSHCD_EH_IN_PROGRESS)
+#define ufshcd_eh_in_progress(h) \
+   (h->eh_flags & UFSHCD_EH_IN_PROGRESS)
+#define ufshcd_clear_eh_in_progress(h) \
+   (h->eh_flags &= ~UFSHCD_EH_IN_PROGRESS)
+
+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
@@ -856,10 +871,25 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *cmd)
 
tag = cmd->request->tag;
 
-   if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   switch (hba->ufshcd_state) {
+   case UFSHCD_STATE_OPERATIONAL:
+   break;
+   case UFSHCD_STATE_RESET:
err = SCSI_MLQUEUE_HOST_BUSY;
-   goto out;
+   goto out_unlock;
+   case UFSHCD_STATE_ERROR:
+   set_host_byte(cmd, DID_ERROR);
+   cmd->scsi_done(cmd);
+   goto out_unlock;
+   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_unlock;
}
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
 
/* acquire the tag to make sure device cmds don't use it */
if (test_and_set_bit_lock(tag, &hba->lrb_in_use)) {
@@ -896,6 +926,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *cmd)
/* issue command to the controller */
spin_lock_irqsave(hba->host->host_lock, flags);
ufshcd_send_command(hba, tag);
+out_unlock:
spin_unlock_irqrestore(hba->host->host_lock, flags);
 out:
return err;
@@ -1707,8 +1738,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;
 }
@@ -2455,8 +2484,12 @@ static void ufshcd_err_handler(struct ufs_hba *hba)
}
return;
 fatal_eh:
-   hba->ufshcd_state = UFSHCD_STATE_ERROR;
-   schedule_work(&hba->feh_workq);
+   /* handle fatal errors only when link is functional */
+   if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) {
+   /* block commands at driver layer until error is handled */
+   hba->ufshcd_state = UFSHCD_STATE_ERROR;
+   schedule_work(&hba->feh_workq);
+   }
 }
 
 /**
@@ -2621,12 +2654,13 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int 
lun_id, int task_id,
 }
 
 /**
- * ufshcd_device_reset - reset device and abort all the pending commands
+ * ufshcd_eh_device_reset_handler - device reset handler registered to
+ *scsi layer.
  * @cmd: SCSI command pointer
  *
  * Returns SUCCESS/FAILED
  */
-static int ufshcd_device_reset(struct scsi_cmnd *cmd)
+static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
 {
struct Scsi_Host *host;
struct ufs_hba *hba;
@@ -2635,6 +2669,7 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd)
int err;
u8 resp = 0xF;
struct ufshcd_lrb *lrbp;
+   unsigned long flags;
 
host = cmd->device->host;
hba = shost_priv(host);
@@ -2643,55 +2678,33 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd)
lrbp = &hba->lrb[tag];
err = ufshcd_issue_tm_cmd(hba, lrbp->

[PATCH V7 3/6] scsi: ufs: Fix broken task management command implementation

2013-09-19 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
---
 drivers/scsi/ufs/ufshcd.c | 169 +++---
 drivers/scsi/ufs/ufshcd.h |   8 ++-
 2 files changed, 122 insertions(+), 55 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index d476cc3..c3acadc 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -55,6 +55,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
 
@@ -182,13 +185,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)
 {
-   return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs);
+   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)
+{
+   clear_bit_unlock(slot, &hba->tm_slots_in_use);
 }
 
 /**
@@ -1912,10 +1937,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;
@@ -1936,19 +1962,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;
 }
 
 /**
@@ -2447,7 +2469

[PATCH V7 2/6] scsi: ufs: make undeclared functions static

2013-09-19 Thread Sujit Reddy Thumma
Make undeclared functions static and declare exported symbols
to suppress warnings from sparse tool.

Signed-off-by: Sujit Reddy Thumma 
---
 drivers/scsi/ufs/ufshcd.c | 4 ++--
 drivers/scsi/ufs/ufshcd.h | 2 ++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 064c9d9..d476cc3 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1148,7 +1148,7 @@ out_unlock:
  *
  * Returns 0 for success, non-zero in case of failure
 */
-int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
+static int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
enum attr_idn idn, u8 index, u8 selector, u32 *attr_val)
 {
struct ufs_query_req *request;
@@ -1459,7 +1459,7 @@ EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
  *
  * Returns 0 on success, non-zero value on failure
  */
-int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
+static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
 {
struct uic_command uic_cmd = {0};
struct completion pwr_done;
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 577679a..767ee9e 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -263,6 +263,8 @@ static inline void check_upiu_size(void)
GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE);
 }
 
+extern int ufshcd_suspend(struct ufs_hba *hba, pm_message_t state);
+extern int ufshcd_resume(struct ufs_hba *hba);
 extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
 extern int ufshcd_runtime_resume(struct ufs_hba *hba);
 extern int ufshcd_runtime_idle(struct ufs_hba *hba);
-- 
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 V7 1/6] scsi: ufs: fix endianness sparse warnings

2013-09-19 Thread Sujit Reddy Thumma
Fix many warnings with incorrect endian assumptions
which makes the code unportable to new architectures.

The UFS specification defines the byte order as big-endian
for UPIU structure and little-endian for the host controller
transfer/task management descriptors.

Signed-off-by: Sujit Reddy Thumma 
---
 drivers/scsi/ufs/ufs.h| 36 ++--
 drivers/scsi/ufs/ufshcd.c | 42 --
 drivers/scsi/ufs/ufshci.h | 32 
 3 files changed, 42 insertions(+), 68 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 7210500..f42d1ce 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -196,9 +196,9 @@ enum {
  * @dword_2: UPIU header DW-2
  */
 struct utp_upiu_header {
-   u32 dword_0;
-   u32 dword_1;
-   u32 dword_2;
+   __be32 dword_0;
+   __be32 dword_1;
+   __be32 dword_2;
 };
 
 /**
@@ -207,7 +207,7 @@ struct utp_upiu_header {
  * @cdb: Command Descriptor Block CDB DW-4 to DW-7
  */
 struct utp_upiu_cmd {
-   u32 exp_data_transfer_len;
+   __be32 exp_data_transfer_len;
u8 cdb[MAX_CDB_SIZE];
 };
 
@@ -228,10 +228,10 @@ struct utp_upiu_query {
u8 idn;
u8 index;
u8 selector;
-   u16 reserved_osf;
-   u16 length;
-   u32 value;
-   u32 reserved[2];
+   __be16 reserved_osf;
+   __be16 length;
+   __be32 value;
+   __be32 reserved[2];
 };
 
 /**
@@ -256,9 +256,9 @@ struct utp_upiu_req {
  * @sense_data: Sense data field DW-8 to DW-12
  */
 struct utp_cmd_rsp {
-   u32 residual_transfer_count;
-   u32 reserved[4];
-   u16 sense_data_len;
+   __be32 residual_transfer_count;
+   __be32 reserved[4];
+   __be16 sense_data_len;
u8 sense_data[18];
 };
 
@@ -286,10 +286,10 @@ struct utp_upiu_rsp {
  */
 struct utp_upiu_task_req {
struct utp_upiu_header header;
-   u32 input_param1;
-   u32 input_param2;
-   u32 input_param3;
-   u32 reserved[2];
+   __be32 input_param1;
+   __be32 input_param2;
+   __be32 input_param3;
+   __be32 reserved[2];
 };
 
 /**
@@ -301,9 +301,9 @@ struct utp_upiu_task_req {
  */
 struct utp_upiu_task_rsp {
struct utp_upiu_header header;
-   u32 output_param1;
-   u32 output_param2;
-   u32 reserved[3];
+   __be32 output_param1;
+   __be32 output_param2;
+   __be32 reserved[3];
 };
 
 /**
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 04884d6..064c9d9 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -163,7 +163,7 @@ static inline int ufshcd_is_device_present(u32 reg_hcs)
  */
 static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
 {
-   return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS;
+   return le32_to_cpu(lrbp->utr_descriptor_ptr->header.dword_2) & MASK_OCS;
 }
 
 /**
@@ -176,7 +176,7 @@ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
 static inline int
 ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp)
 {
-   return task_req_descp->header.dword_2 & MASK_OCS;
+   return le32_to_cpu(task_req_descp->header.dword_2) & MASK_OCS;
 }
 
 /**
@@ -390,26 +390,6 @@ static inline void ufshcd_copy_sense_data(struct 
ufshcd_lrb *lrbp)
 }
 
 /**
- * ufshcd_query_to_cpu() - formats the buffer to native cpu endian
- * @response: upiu query response to convert
- */
-static inline void ufshcd_query_to_cpu(struct utp_upiu_query *response)
-{
-   response->length = be16_to_cpu(response->length);
-   response->value = be32_to_cpu(response->value);
-}
-
-/**
- * ufshcd_query_to_be() - formats the buffer to big endian
- * @request: upiu query request to convert
- */
-static inline void ufshcd_query_to_be(struct utp_upiu_query *request)
-{
-   request->length = cpu_to_be16(request->length);
-   request->value = cpu_to_be32(request->value);
-}
-
-/**
  * ufshcd_copy_query_response() - Copy the Query Response and the data
  * descriptor
  * @hba: per adapter instance
@@ -425,7 +405,6 @@ void ufshcd_copy_query_response(struct ufs_hba *hba, struct 
ufshcd_lrb *lrbp)
UPIU_RSP_CODE_OFFSET;
 
memcpy(&query_res->upiu_res, &lrbp->ucd_rsp_ptr->qr, QUERY_OSF_SIZE);
-   ufshcd_query_to_cpu(&query_res->upiu_res);
 
 
/* Get the descriptor */
@@ -749,7 +728,7 @@ static void ufshcd_prepare_utp_query_req_upiu(struct 
ufs_hba *hba,
 {
struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
struct ufs_query *query = &hba->dev_cmd.query;
-   u16 len = query->request.upiu_req.length;
+   u16 len = be16_to_cpu(query->request.upiu_req.length);
u8 *descp = (u8 *)lrbp->ucd_req_ptr + GENERAL_UPIU_REQUEST_SIZE;
 
/* Query request header */
@@ -766,7 +745,6 @@ static void ufshcd_prepare_utp_query_req_upiu(st

[PATCH V7 0/6] scsi: ufs: TM, fatal-error handling and other fixes

2013-09-19 Thread Sujit Reddy Thumma
This patch version fixes various static checker warnings along with fixing
usage of deprecated flush_work_sync() API that led James drop the patchset
from scsi-misc tree.

The first patch fixes many issues with current task management handling
in UFSHCD driver. Others improve error handling in various scenarios.

These patches are rebased on scsi-misc.

Changes from v6:
- Two new patches to fix static checker warnings
- remove usage of deprecated flush_work_sync() API.
Changes from v5:
- correct typo in [PATCH V5 1/4] scsi: ufs: Fix broken task ...
- correct poll wait in [PATCH V5 2/4] scsi: ufs: Fix hardware ...
- Included Tested-by and Reviewed-by ack's.
Changes from v4:
- Addressed comments from Seungwon Jeon on V3
- Updated with proper locking while ufshcd state changes
- Retained LUN reset instead of device reset with DME_END_POINT_RESET
- Removed error handling decisions dependent on OCS value
- Simplified fatal error handling to abort the requests first
  and then carry out reset.
Changes from v3:
- Rebased.
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 (6):
  scsi: ufs: fix endianness sparse warnings
  scsi: ufs: make undeclared functions static
  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/ufs.h|  36 +--
 drivers/scsi/ufs/ufshcd.c | 722 +++---
 drivers/scsi/ufs/ufshcd.h |  22 +-
 drivers/scsi/ufs/ufshci.h |  32 +-
 4 files changed, 545 insertions(+), 267 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 V2 2/3] scsi: ufs: Add regulator enable support

2013-08-26 Thread Sujit Reddy Thumma
UFS devices are powered by at most three external power supplies -
- VCC - The flash memory core power supply, 2.7V to 3.6V or 1.70V to 1.95V
- VCCQ - The controller and I/O power supply, 1.1V to 1.3V
- VCCQ2 - Secondary controller and/or I/O power supply, 1.65V to 1.95V

For some devices VCCQ or VCCQ2 are optional as they can be
generated using internal LDO inside the UFS device.

Add DT bindings for voltage regulators that can be controlled
from host driver.

Signed-off-by: Sujit Reddy Thumma 
---
 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |  24 +++
 drivers/scsi/ufs/ufs.h |  25 +++
 drivers/scsi/ufs/ufshcd-pltfrm.c   | 100 +++
 drivers/scsi/ufs/ufshcd.c  | 193 -
 drivers/scsi/ufs/ufshcd.h  |   3 +
 5 files changed, 342 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt 
b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 20468b2..65e3117 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -8,9 +8,33 @@ Required properties:
 - interrupts: 
 - reg   : 
 
+Optional properties:
+- vcc-supply: phandle to VCC supply regulator node
+- vccq-supply   : phandle to VCCQ supply regulator node
+- vccq2-supply  : phandle to VCCQ2 supply regulator node
+- vcc-supply-1p8: For embedded UFS devices, valid VCC range is 
1.7-1.95V
+  or 2.7-3.6V. This boolean property when set, 
specifies
+ to use low voltage range of 1.7-1.95V. Note for 
external
+ UFS cards this property is invalid and valid VCC 
range is
+ always 2.7-3.6V.
+- vcc-max-microamp  : specifies max. load that can be drawn from vcc supply
+- vccq-max-microamp : specifies max. load that can be drawn from vccq 
supply
+- vccq2-max-microamp: specifies max. load that can be drawn from vccq2 
supply
+
+Note: If above properties are not defined it can be assumed that the supply
+regulators are always on.
+
 Example:
ufshc@0xfc598000 {
compatible = "jedec,ufs-1.1";
reg = <0xfc598000 0x800>;
interrupts = <0 28 0>;
+
+   vcc-supply = <&xxx_reg1>;
+   vcc-supply-1p8;
+   vccq-supply = <&xxx_reg2>;
+   vccq2-supply = <&xxx_reg3>;
+   vcc-max-microamp = 50;
+   vccq-max-microamp = 20;
+   vccq2-max-microamp = 20;
};
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index bce09a6..7db080e 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -325,4 +325,29 @@ struct ufs_query_res {
struct utp_upiu_query upiu_res;
 };
 
+#define UFS_VREG_VCC_MIN_UV   270 /* uV */
+#define UFS_VREG_VCC_MAX_UV   360 /* uV */
+#define UFS_VREG_VCC_1P8_MIN_UV170 /* uV */
+#define UFS_VREG_VCC_1P8_MAX_UV195 /* uV */
+#define UFS_VREG_VCCQ_MIN_UV  110 /* uV */
+#define UFS_VREG_VCCQ_MAX_UV  130 /* uV */
+#define UFS_VREG_VCCQ2_MIN_UV 165 /* uV */
+#define UFS_VREG_VCCQ2_MAX_UV 195 /* uV */
+
+struct ufs_vreg {
+   struct regulator *reg;
+   const char *name;
+   bool enabled;
+   int min_uV;
+   int max_uV;
+   int min_uA;
+   int max_uA;
+};
+
+struct ufs_vreg_info {
+   struct ufs_vreg *vcc;
+   struct ufs_vreg *vccq;
+   struct ufs_vreg *vccq2;
+};
+
 #endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 9c94052..cbdf5f3 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -52,6 +52,99 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct 
device *dev)
return NULL;
 }
 
+#define MAX_PROP_SIZE 32
+static int ufshcd_populate_vreg(struct device *dev, const char *name,
+   struct ufs_vreg **out_vreg)
+{
+   int ret = 0;
+   char prop_name[MAX_PROP_SIZE];
+   struct ufs_vreg *vreg = NULL;
+   struct device_node *np = dev->of_node;
+
+   if (!np) {
+   dev_err(dev, "%s: non DT initialization\n", __func__);
+   goto out;
+   }
+
+   snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name);
+   if (!of_parse_phandle(np, prop_name, 0)) {
+   dev_info(dev, "%s: Unable to find %s regulator, assuming 
enabled\n",
+   __func__, prop_name);
+   goto out;
+   }
+
+   vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+   if (!vreg) {
+   dev_err(dev, "No memory for %s regulator\n", name);
+   goto out;
+   }
+
+   vreg->name

[PATCH V2 0/3] scsi: ufs: Add support for clock and regulator initializaiton

2013-08-26 Thread Sujit Reddy Thumma
UFS HCI specificaiton allows the hardware vendors to have vendor specific
configurations using a dedicated register space. Most of these register
configurations are unique to each controller. The generic UFSHCD exports
a set of useful vendor operations that can be used to configure the host
controller. In addition, the vendor specific operations can also be used
to control the power supply of controller and Uni-Pro h/w blocks.

This patchset adds support for vendor specific initialization, regulator
and clock initialization based on device-tree properties.

Changes from v1:
 - Rebased on scsi-misc.
 - Minor code fixes.

Sujit Reddy Thumma (3):
  scsi: ufs: Allow vendor specific initialization
  scsi: ufs: Add regulator enable support
  scsi: ufs: Add clock initialization support

 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |  37 ++
 drivers/scsi/ufs/ufs.h |  25 ++
 drivers/scsi/ufs/ufshcd-pci.c  |   8 +-
 drivers/scsi/ufs/ufshcd-pltfrm.c   | 195 +-
 drivers/scsi/ufs/ufshcd.c  | 429 +++--
 drivers/scsi/ufs/ufshcd.h  |  55 ++-
 6 files changed, 713 insertions(+), 36 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 V2 3/3] scsi: ufs: Add clock initialization support

2013-08-26 Thread Sujit Reddy Thumma
Add generic clock initialization support for UFSHCD platform
driver. The clock info is read from device tree using standard
clock bindings. A generic max-clock-frequency-hz property is
defined to save information on maximum operating clock frequency
the h/w supports.

Signed-off-by: Sujit Reddy Thumma 
---
 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  | 15 +++-
 drivers/scsi/ufs/ufshcd-pltfrm.c   | 71 +
 drivers/scsi/ufs/ufshcd.c  | 89 +-
 drivers/scsi/ufs/ufshcd.h  | 18 +
 4 files changed, 190 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt 
b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 65e3117..b0f791a 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -21,8 +21,17 @@ Optional properties:
 - vccq-max-microamp : specifies max. load that can be drawn from vccq 
supply
 - vccq2-max-microamp: specifies max. load that can be drawn from vccq2 
supply
 
+- clocks: List of phandle and clock specifier pairs
+- clock-names   : List of clock input name strings sorted in the same
+  order as the clocks property.
+- max-clock-frequency-hz : List of maximum operating frequency stored in the 
same
+   order as the clocks property. If this property is 
not
+  defined or a value in the array is "0" then it is 
assumed
+  that the frequency is set by the parent clock or a
+  fixed rate clock source.
+
 Note: If above properties are not defined it can be assumed that the supply
-regulators are always on.
+regulators or clocks are always on.
 
 Example:
ufshc@0xfc598000 {
@@ -37,4 +46,8 @@ Example:
vcc-max-microamp = 50;
vccq-max-microamp = 20;
vccq2-max-microamp = 20;
+
+   clocks = <&core 0>, <&ref 0>, <&iface 0>;
+   clock-names = "core_clk", "ref_clk", "iface_clk";
+   max-clock-frequency-hz = <1 1920 0>;
};
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index cbdf5f3..15c8086 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -52,6 +52,71 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct 
device *dev)
return NULL;
 }
 
+static int ufshcd_parse_clock_info(struct ufs_hba *hba)
+{
+   int ret = 0;
+   int cnt;
+   int i;
+   struct device *dev = hba->dev;
+   struct device_node *np = dev->of_node;
+   char *name;
+   u32 *clkfreq = NULL;
+   struct ufs_clk_info *clki;
+
+   if (!np)
+   goto out;
+
+   INIT_LIST_HEAD(&hba->clk_list_head);
+
+   cnt = of_property_count_strings(np, "clock-names");
+   if (!cnt || (cnt == -EINVAL)) {
+   dev_info(dev, "%s: Unable to find clocks, assuming enabled\n",
+   __func__);
+   } else if (cnt < 0) {
+   dev_err(dev, "%s: count clock strings failed, err %d\n",
+   __func__, cnt);
+   ret = cnt;
+   }
+
+   if (cnt <= 0)
+   goto out;
+
+   clkfreq = kzalloc(cnt * sizeof(*clkfreq), GFP_KERNEL);
+   if (!clkfreq) {
+   ret = -ENOMEM;
+   dev_err(dev, "%s: memory alloc failed\n", __func__);
+   goto out;
+   }
+
+   ret = of_property_read_u32_array(np,
+   "max-clock-frequency-hz", clkfreq, cnt);
+   if (ret && (ret != -EINVAL)) {
+   dev_err(dev, "%s: invalid max-clock-frequency-hz property, 
%d\n",
+   __func__, ret);
+   goto out;
+   }
+
+   for (i = 0; i < cnt; i++) {
+   ret = of_property_read_string_index(np,
+   "clock-names", i, (const char **)&name);
+   if (ret)
+   goto out;
+
+   clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
+   if (!clki) {
+   ret = -ENOMEM;
+   goto out;
+   }
+
+   clki->max_freq = clkfreq[i];
+   clki->name = kstrdup(name, GFP_KERNEL);
+   list_add_tail(&clki->list, &hba->clk_list_head);
+   }
+out:
+   kfree(clkfreq);
+   return ret;
+}
+
 #define MAX_PROP_SIZE 32
 static int ufshcd_populate_vreg(struct device *dev, const char *name,
struct ufs_vreg **out_vreg)
@@ -265,6 +330,12 @@ static int ufshcd_pltfrm_probe(struct

[PATCH V2 1/3] scsi: ufs: Allow vendor specific initialization

2013-08-26 Thread Sujit Reddy Thumma
Some vendor specific controller versions might need to configure
vendor specific - registers, clocks, voltage regulators etc. to
initialize the host controller UTP layer and Uni-Pro stack.
Provide some common initialization operations that can be used
to configure vendor specifics. The methods can be extended in
future, for example, for power mode transitions.

The operations are vendor/board specific and hence determined with
the help of compatible property in device tree.

Signed-off-by: Sujit Reddy Thumma 
---
 drivers/scsi/ufs/ufshcd-pci.c|   8 +-
 drivers/scsi/ufs/ufshcd-pltfrm.c |  24 +-
 drivers/scsi/ufs/ufshcd.c| 157 +++
 drivers/scsi/ufs/ufshcd.h|  34 -
 4 files changed, 187 insertions(+), 36 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
index a823cf4..6b0d299 100644
--- a/drivers/scsi/ufs/ufshcd-pci.c
+++ b/drivers/scsi/ufs/ufshcd-pci.c
@@ -191,7 +191,13 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct 
pci_device_id *id)
return err;
}
 
-   err = ufshcd_init(&pdev->dev, &hba, mmio_base, pdev->irq);
+   err = ufshcd_alloc_host(&pdev->dev, &hba);
+   if (err) {
+   dev_err(&pdev->dev, "Allocation failed\n");
+   return err;
+   }
+
+   err = ufshcd_init(hba, mmio_base, pdev->irq);
if (err) {
dev_err(&pdev->dev, "Initialization failed\n");
return err;
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 5e46232..9c94052 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -35,9 +35,23 @@
 
 #include 
 #include 
+#include 
 
 #include "ufshcd.h"
 
+static const struct of_device_id ufs_of_match[];
+static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
+{
+   if (dev->of_node) {
+   const struct of_device_id *match;
+   match = of_match_node(ufs_of_match, dev->of_node);
+   if (match)
+   return (struct ufs_hba_variant_ops *)match->data;
+   }
+
+   return NULL;
+}
+
 #ifdef CONFIG_PM
 /**
  * ufshcd_pltfrm_suspend - suspend power management function
@@ -150,10 +164,18 @@ static int ufshcd_pltfrm_probe(struct platform_device 
*pdev)
goto out;
}
 
+   err = ufshcd_alloc_host(dev, &hba);
+   if (err) {
+   dev_err(&pdev->dev, "Allocation failed\n");
+   goto out;
+   }
+
+   hba->vops = get_variant_ops(&pdev->dev);
+
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
 
-   err = ufshcd_init(dev, &hba, mmio_base, irq);
+   err = ufshcd_init(hba, mmio_base, irq);
if (err) {
dev_err(dev, "Intialization failed\n");
goto out_disable_rpm;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a0f5ac2..743696a 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -174,13 +174,14 @@ static inline u32 ufshcd_get_ufs_version(struct ufs_hba 
*hba)
 /**
  * ufshcd_is_device_present - Check if any device connected to
  *   the host controller
- * @reg_hcs - host controller status register value
+ * @hba: pointer to adapter instance
  *
  * Returns 1 if device present, 0 if no device detected
  */
-static inline int ufshcd_is_device_present(u32 reg_hcs)
+static inline int ufshcd_is_device_present(struct ufs_hba *hba)
 {
-   return (DEVICE_PRESENT & reg_hcs) ? 1 : 0;
+   return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) &
+   DEVICE_PRESENT) ? 1 : 0;
 }
 
 /**
@@ -1483,11 +1484,10 @@ out:
  * @hba: per adapter instance
  *
  * To bring UFS host controller to operational state,
- * 1. Check if device is present
- * 2. Enable required interrupts
- * 3. Configure interrupt aggregation
- * 4. Program UTRL and UTMRL base addres
- * 5. Configure run-stop-registers
+ * 1. Enable required interrupts
+ * 2. Configure interrupt aggregation
+ * 3. Program UTRL and UTMRL base addres
+ * 4. Configure run-stop-registers
  *
  * Returns 0 on success, non-zero value on failure
  */
@@ -1496,14 +1496,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba 
*hba)
int err = 0;
u32 reg;
 
-   /* check if device present */
-   reg = ufshcd_readl(hba, REG_CONTROLLER_STATUS);
-   if (!ufshcd_is_device_present(reg)) {
-   dev_err(hba->dev, "cc: Device not present\n");
-   err = -ENXIO;
-   goto out;
-   }
-
/* Enable required interrupts */
ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
 
@@ -1524,6 +1516,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba 
*hba)
 

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

2013-08-14 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 are rebased on:
[PATCH 9/9] drivers/scsi/ufs: don't check resource with devm_ioremap_resource

Changes from v5:
- correct typo in [PATCH V5 1/4] scsi: ufs: Fix broken task ...
- correct poll wait in [PATCH V5 2/4] scsi: ufs: Fix hardware ...
- Included Tested-by and Reviewed-by ack's.
Changes from v4:
- Addressed comments from Seungwon Jeon on V3
- Updated with proper locking while ufshcd state changes
- Retained LUN reset instead of device reset with DME_END_POINT_RESET
- Removed error handling decisions dependent on OCS value
- Simplified fatal error handling to abort the requests first
  and then carry out reset.
Changes from v3:
- Rebased.
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 |  684 -
 drivers/scsi/ufs/ufshcd.h |   20 +-
 2 files changed, 501 insertions(+), 203 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 V6 3/4] scsi: ufs: Fix device and host reset methods

2013-08-14 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
---
 drivers/scsi/ufs/ufshcd.c |  240 +++--
 drivers/scsi/ufs/ufshcd.h |2 +
 2 files changed, 189 insertions(+), 53 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 24c5ab3..dfa61be 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -69,9 +69,14 @@ enum {
 
 /* UFSHCD states */
 enum {
-   UFSHCD_STATE_OPERATIONAL,
UFSHCD_STATE_RESET,
UFSHCD_STATE_ERROR,
+   UFSHCD_STATE_OPERATIONAL,
+};
+
+/* UFSHCD error handling flags */
+enum {
+   UFSHCD_EH_IN_PROGRESS = (1 << 0),
 };
 
 /* Interrupt configuration options */
@@ -87,6 +92,16 @@ enum {
INT_AGGR_CONFIG,
 };
 
+#define ufshcd_set_eh_in_progress(h) \
+   (h->eh_flags |= UFSHCD_EH_IN_PROGRESS)
+#define ufshcd_eh_in_progress(h) \
+   (h->eh_flags & UFSHCD_EH_IN_PROGRESS)
+#define ufshcd_clear_eh_in_progress(h) \
+   (h->eh_flags &= ~UFSHCD_EH_IN_PROGRESS)
+
+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
@@ -840,10 +855,25 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *cmd)
 
tag = cmd->request->tag;
 
-   if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   switch (hba->ufshcd_state) {
+   case UFSHCD_STATE_OPERATIONAL:
+   break;
+   case UFSHCD_STATE_RESET:
err = SCSI_MLQUEUE_HOST_BUSY;
-   goto out;
+   goto out_unlock;
+   case UFSHCD_STATE_ERROR:
+   set_host_byte(cmd, DID_ERROR);
+   cmd->scsi_done(cmd);
+   goto out_unlock;
+   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_unlock;
}
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
 
/* acquire the tag to make sure device cmds don't use it */
if (test_and_set_bit_lock(tag, &hba->lrb_in_use)) {
@@ -880,6 +910,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *cmd)
/* issue command to the controller */
spin_lock_irqsave(hba->host->host_lock, flags);
ufshcd_send_command(hba, tag);
+out_unlock:
spin_unlock_irqrestore(hba->host->host_lock, flags);
 out:
return err;
@@ -1495,8 +1526,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;
 }
@@ -2245,8 +2274,12 @@ static void ufshcd_err_handler(struct ufs_hba *hba)
}
return;
 fatal_eh:
-   hba->ufshcd_state = UFSHCD_STATE_ERROR;
-   schedule_work(&hba->feh_workq);
+   /* handle fatal errors only when link is functional */
+   if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) {
+   /* block commands at driver layer until error is handled */
+   hba->ufshcd_state = UFSHCD_STATE_ERROR;
+   schedule_work(&hba->feh_workq);
+   }
 }
 
 /**
@@ -2411,12 +2444,13 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int 
lun_id, int task_id,
 }
 
 /**
- * ufshcd_device_reset - reset device and abort all the pending commands
+ * ufshcd_eh_device_reset_handler - device reset handler registered to
+ *scsi layer.
  * @cmd: SCSI command pointer
  *
  * Returns SUCCESS/FAILED
  */
-static int ufshcd_device_reset(struct scsi_cmnd *cmd)
+static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
 {
struct Scsi_Host *host;
struct ufs_hba *hba;
@@ -2425,6 +2459,7 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd)
int err;
u8 resp = 0xF;
struct ufshcd_lrb *lrbp;
+   unsigned long flags;
 
host = cmd->device->host;
hba = shost_priv(host);
@@ -2433,55 +2468,33 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd)
lrbp = &hba->lrb[tag];
err = ufshcd_issue_tm_cmd(hba, lrbp->

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

2013-08-14 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
---
 drivers/scsi/ufs/ufshcd.c |  173 ++---
 drivers/scsi/ufs/ufshcd.h |8 ++-
 2 files changed, 122 insertions(+), 59 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index b36ca9a..e0810a3 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
 
@@ -183,13 +186,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)
 {
-   return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs);
+   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)
+{
+   clear_bit_unlock(slot, &hba->tm_slots_in_use);
 }
 
 /**
@@ -1700,10 +1725,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;
@@ -1724,19 +1750,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;
 }
 
 /**
@@ -2237,7 +2259

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

2013-08-14 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
---
 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 e0810a3..24c5ab3 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2485,6 +2485,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)
@@ -2493,7 +2499,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 = 0xF;
struct ufshcd_lrb *lrbp;
 
@@ -2501,33 +2508,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. 200us to stabilize */
+   usleep_range(100, 200);
+   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);
 

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

2013-08-14 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 Processed requests which are completed w/wo error are reported to
  SCSI layer and any pending commands that are not started are aborted
  in the controller and re-queued into scsi mid-layer queue.

o Upon determining fatal error condition the host controller may hang
  forever until a reset is applied. Block SCSI layer for sending new
  requests and apply reset in a separate error handling work.

o SCSI is informed about the expected Unit-Attention 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 
Reviewed-by: Yaniv Gardi 
Tested-by: Dolev Raviv 
---
 drivers/scsi/ufs/ufshcd.c |  229 -
 drivers/scsi/ufs/ufshcd.h |   10 ++-
 2 files changed, 149 insertions(+), 90 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index dfa61be..a0f5ac2 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -79,6 +79,14 @@ enum {
UFSHCD_EH_IN_PROGRESS = (1 << 0),
 };
 
+/* 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,
@@ -101,6 +109,8 @@ 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);
+static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag);
 
 /*
  * ufshcd_wait_for_register - wait for register value to change
@@ -1523,9 +1533,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;
 }
@@ -1651,66 +1658,6 @@ static int ufshcd_verify_dev_init(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
  *
@@ -1727,6 +1674,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
@@ -1930

Re: [PATCH 1/3] scsi: ufs: Allow vendor specific initialization

2013-08-13 Thread Sujit Reddy Thumma

On 8/13/2013 7:23 PM, Josh Cartwright wrote:

On Tue, Aug 13, 2013 at 04:30:18PM +0530, Sujit Reddy Thumma wrote:
[..]

diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
index a823cf4..829f7a4 100644
--- a/drivers/scsi/ufs/ufshcd-pci.c
+++ b/drivers/scsi/ufs/ufshcd-pci.c
@@ -191,7 +191,13 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct 
pci_device_id *id)
return err;
}

-   err = ufshcd_init(&pdev->dev, &hba, mmio_base, pdev->irq);
+   err = ufshcd_alloc_host(&pdev->dev, &hba);
+   if (err) {
+   dev_err(&pdev->dev, "Allocation failed\n");
+   goto out_iounmap


You seem to be missing a semicolon here.

I should have compiled the with pci config enabled :)



Also, which tree were these patches generated against?  They fail to
apply at least on the last few 3.11-rc's.


I have rebased on top of scsi-misc branch along with few UFS patches 
that aren't merged ([PATCH 0/9] scsi:ufs: query, bkops support and

other fixes + [PATCH V5 0/4] scsi: ufs: Improve UFS error handling
patch series).


--
Regards,
Sujit
--
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 3/3] scsi: ufs: Add clock initialization support

2013-08-13 Thread Sujit Reddy Thumma
Add generic clock initialization support for UFSHCD platform
driver. The clock info is read from device tree using standard
clock bindings. A generic max-clock-frequency-hz property is
defined to save information on maximum operating clock frequency
the h/w supports.

Signed-off-by: Sujit Reddy Thumma 
---
 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |   15 +++-
 drivers/scsi/ufs/ufshcd-pltfrm.c   |   64 ++
 drivers/scsi/ufs/ufshcd.c  |   90 +++-
 drivers/scsi/ufs/ufshcd.h  |   18 
 4 files changed, 184 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt 
b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 39ee11f..d82c54c 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -16,8 +16,17 @@ Optional properties:
 - vccq-max-microamp : specifies max. load that can be drawn from vccq 
supply
 - vccq2-max-microamp: specifies max. load that can be drawn from vccq2 
supply
 
+- clocks: List of phandle and clock specifier pairs
+- clock-names   : List of clock input name strings sorted in the same
+  order as the clocks property.
+- max-clock-frequency-hz : List of maximum operating frequency stored in the 
same
+   order as the clocks property. If this property is 
not
+  defined or a value in the array is "0" then it is 
assumed
+  that the frequency is set by the parent clock or a
+  fixed rate clock source.
+
 Note: If above properties are not defined it can be assumed that the supply
-regulators are always on.
+regulators or clocks are always on.
 
 Example:
ufshc@0xfc598000 {
@@ -31,4 +40,8 @@ Example:
vcc-max-microamp = 50;
vccq-max-microamp = 20;
vccq2-max-microamp = 20;
+
+   clocks = <&core 0>, <&ref 0>, <&iface 0>;
+   clock-names = "core_clk", "ref_clk", "iface_clk";
+   max-clock-frequency-hz = <1 1920 0>;
};
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 1c5290d..9bbcbcb 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -38,6 +38,7 @@
 #include 
 
 #include "ufshcd.h"
+#define MAX_CLK_CNT100
 
 static const struct of_device_id ufs_of_match[];
 static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
@@ -51,6 +52,63 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct 
device *dev)
return NULL;
 }
 
+static int ufshcd_parse_clock_info(struct ufs_hba *hba)
+{
+   int ret = 0;
+   int cnt;
+   int i;
+   struct device *dev = hba->dev;
+   struct device_node *np = dev->of_node;
+   char *name;
+   u32 clkfreq[MAX_CLK_CNT] = {0};
+   struct ufs_clk_info *clki;
+
+   if (!np)
+   goto out;
+
+   INIT_LIST_HEAD(&hba->clk_list_head);
+
+   cnt = of_property_count_strings(np, "clock-names");
+   if (!cnt || (cnt == -EINVAL)) {
+   dev_info(dev, "%s: Unable to find clocks, assuming enabled\n",
+   __func__);
+   } else if (cnt < 0) {
+   dev_err(dev, "%s: count clock strings failed, err %d\n",
+   __func__, cnt);
+   ret = cnt;
+   }
+
+   if (cnt <= 0)
+   goto out;
+
+   ret = of_property_read_u32_array(np,
+   "max-clock-frequency-hz", clkfreq, cnt);
+   if (ret && (ret != -EINVAL)) {
+   dev_err(dev, "%s: invalid max-clock-frequency-hz property, 
%d\n",
+   __func__, ret);
+   goto out;
+   }
+
+   for (i = 0; i < cnt; i++) {
+   ret = of_property_read_string_index(np,
+   "clock-names", i, (const char **)&name);
+   if (ret)
+   goto out;
+
+   clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
+   if (!clki) {
+   ret = -ENOMEM;
+   goto out;
+   }
+
+   clki->max_freq = clkfreq[i];
+   clki->name = kstrdup(name, GFP_KERNEL);
+   list_add_tail(&clki->list, &hba->clk_list_head);
+   }
+out:
+   return ret;
+}
+
 #define MAX_PROP_SIZE 32
 static int ufshcd_populate_vreg(struct device *dev, const char *name,
struct ufs_vreg **out_vreg)
@@ -253,6 +311,12 @@ static int ufshcd_pltfrm_probe(struct plat

[PATCH 1/3] scsi: ufs: Allow vendor specific initialization

2013-08-13 Thread Sujit Reddy Thumma
Some vendor specific controller versions might need to configure
vendor specific - registers, clocks, voltage regulators etc. to
initialize the host controller UTP layer and Uni-Pro stack.
Provide some common initialization operations that can be used
to configure vendor specifics. The methods can be extended in
future, for example, for power mode transitions.

The operations are vendor/board specific and hence determined with
the help of compatible property in device tree.

Signed-off-by: Sujit Reddy Thumma 
---
 drivers/scsi/ufs/ufshcd-pci.c|8 ++-
 drivers/scsi/ufs/ufshcd-pltfrm.c |   23 +-
 drivers/scsi/ufs/ufshcd.c|  157 ++
 drivers/scsi/ufs/ufshcd.h|   34 -
 4 files changed, 185 insertions(+), 37 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
index a823cf4..829f7a4 100644
--- a/drivers/scsi/ufs/ufshcd-pci.c
+++ b/drivers/scsi/ufs/ufshcd-pci.c
@@ -191,7 +191,13 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct 
pci_device_id *id)
return err;
}
 
-   err = ufshcd_init(&pdev->dev, &hba, mmio_base, pdev->irq);
+   err = ufshcd_alloc_host(&pdev->dev, &hba);
+   if (err) {
+   dev_err(&pdev->dev, "Allocation failed\n");
+   goto out_iounmap
+   }
+
+   err = ufshcd_init(hba, mmio_base, pdev->irq);
if (err) {
dev_err(&pdev->dev, "Initialization failed\n");
return err;
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 5e46232..3e0a14f 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -35,9 +35,22 @@
 
 #include 
 #include 
+#include 
 
 #include "ufshcd.h"
 
+static const struct of_device_id ufs_of_match[];
+static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
+{
+   if (dev->of_node) {
+   const struct of_device_id *match;
+   match = of_match_node(ufs_of_match, dev->of_node);
+   return (struct ufs_hba_variant_ops *)match->data;
+   }
+
+   return NULL;
+}
+
 #ifdef CONFIG_PM
 /**
  * ufshcd_pltfrm_suspend - suspend power management function
@@ -150,10 +163,18 @@ static int ufshcd_pltfrm_probe(struct platform_device 
*pdev)
goto out;
}
 
+   err = ufshcd_alloc_host(dev, &hba);
+   if (err) {
+   dev_err(&pdev->dev, "Allocation failed\n");
+   goto out;
+   }
+
+   hba->vops = get_variant_ops(&pdev->dev);
+
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
 
-   err = ufshcd_init(dev, &hba, mmio_base, irq);
+   err = ufshcd_init(hba, mmio_base, irq);
if (err) {
dev_err(dev, "Intialization failed\n");
goto out_disable_rpm;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 4dca9b4..c539347 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -174,13 +174,14 @@ static inline u32 ufshcd_get_ufs_version(struct ufs_hba 
*hba)
 /**
  * ufshcd_is_device_present - Check if any device connected to
  *   the host controller
- * @reg_hcs - host controller status register value
+ * @hba: pointer to adapter instance
  *
  * Returns 1 if device present, 0 if no device detected
  */
-static inline int ufshcd_is_device_present(u32 reg_hcs)
+static inline int ufshcd_is_device_present(struct ufs_hba *hba)
 {
-   return (DEVICE_PRESENT & reg_hcs) ? 1 : 0;
+   return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) &
+   DEVICE_PRESENT) ? 1 : 0;
 }
 
 /**
@@ -1483,11 +1484,10 @@ out:
  * @hba: per adapter instance
  *
  * To bring UFS host controller to operational state,
- * 1. Check if device is present
- * 2. Enable required interrupts
- * 3. Configure interrupt aggregation
- * 4. Program UTRL and UTMRL base addres
- * 5. Configure run-stop-registers
+ * 1. Enable required interrupts
+ * 2. Configure interrupt aggregation
+ * 3. Program UTRL and UTMRL base addres
+ * 4. Configure run-stop-registers
  *
  * Returns 0 on success, non-zero value on failure
  */
@@ -1496,14 +1496,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba 
*hba)
int err = 0;
u32 reg;
 
-   /* check if device present */
-   reg = ufshcd_readl(hba, REG_CONTROLLER_STATUS);
-   if (!ufshcd_is_device_present(reg)) {
-   dev_err(hba->dev, "cc: Device not present\n");
-   err = -ENXIO;
-   goto out;
-   }
-
/* Enable required interrupts */
ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
 
@@ -1524,6 +1516,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba 
*hba)
 * UCRDY, UTMRLDY and UT

[PATCH 0/3] scsi: ufs: Add support for clock and regulator initializaiton

2013-08-13 Thread Sujit Reddy Thumma
UFS HCI specificaiton allows the hardware vendors to have vendor specific
configurations using a dedicated register space. Most of these register
configurations are unique to each controller. The generic UFSHCD exports
a set of useful vendor operations that can be used to configure the host
controller. In addition, the vendor specific operations can also be used
to control the power supply of controller and Uni-Pro h/w blocks.

This patchset adds support for vendor specific initialization, regulator
and clock initialization based on device-tree properties.

Sujit Reddy Thumma (3):
  scsi: ufs: Allow vendor specific initialization
  scsi: ufs: Add regulator enable support
  scsi: ufs: Add clock initialization support

 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |   31 ++
 drivers/scsi/ufs/ufs.h |   23 +
 drivers/scsi/ufs/ufshcd-pci.c  |8 +-
 drivers/scsi/ufs/ufshcd-pltfrm.c   |  176 -
 drivers/scsi/ufs/ufshcd.c  |  433 ++--
 drivers/scsi/ufs/ufshcd.h  |   55 +++-
 6 files changed, 689 insertions(+), 37 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 2/3] scsi: ufs: Add regulator enable support

2013-08-13 Thread Sujit Reddy Thumma
UFS devices are powered by at most three external power supplies -
- VCC - The flash memory core power supply, 2.7V to 3.6V or 1.70V to 1.95V
- VCCQ - The controller and I/O power supply, 1.1V to 1.3V
- VCCQ2 - Secondary controller and/or I/O power supply, 1.65V to 1.95V

For some devices VCCQ or VCCQ2 are optional as they can be
generated using internal LDO inside the UFS device.

Add DT bindings for voltage regulators that can be controlled
from host driver.

Signed-off-by: Sujit Reddy Thumma 
---
 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |   18 ++
 drivers/scsi/ufs/ufs.h |   23 +++
 drivers/scsi/ufs/ufshcd-pltfrm.c   |   89 +
 drivers/scsi/ufs/ufshcd.c  |  194 +++-
 drivers/scsi/ufs/ufshcd.h  |3 +
 5 files changed, 325 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt 
b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 20468b2..39ee11f 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -8,9 +8,27 @@ Required properties:
 - interrupts: 
 - reg   : 
 
+Optional properties:
+- vcc-supply: phandle to VCC supply regulator node
+- vccq-supply   : phandle to VCCQ supply regulator node
+- vccq2-supply  : phandle to VCCQ2 supply regulator node
+- vcc-max-microamp  : specifies max. load that can be drawn from vcc supply
+- vccq-max-microamp : specifies max. load that can be drawn from vccq 
supply
+- vccq2-max-microamp: specifies max. load that can be drawn from vccq2 
supply
+
+Note: If above properties are not defined it can be assumed that the supply
+regulators are always on.
+
 Example:
ufshc@0xfc598000 {
compatible = "jedec,ufs-1.1";
reg = <0xfc598000 0x800>;
interrupts = <0 28 0>;
+
+   vcc-supply = <&xxx_reg1>;
+   vccq-supply = <&xxx_reg2>;
+   vccq2-supply = <&xxx_reg3>;
+   vcc-max-microamp = 50;
+   vccq-max-microamp = 20;
+   vccq2-max-microamp = 20;
};
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index bce09a6..f8e37bf 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -325,4 +325,27 @@ struct ufs_query_res {
struct utp_upiu_query upiu_res;
 };
 
+#define UFS_VREG_VCC_MIN   270 /* uV */
+#define UFS_VREG_VCC_MAX   360 /* uV */
+#define UFS_VREG_VCCQ_MIN  115 /* uV */
+#define UFS_VREG_VCCQ_MAX  130 /* uV */
+#define UFS_VREG_VCCQ2_MIN 170 /* uV */
+#define UFS_VREG_VCCQ2_MAX 195 /* uV */
+
+struct ufs_vreg {
+   struct regulator *reg;
+   const char *name;
+   bool enabled;
+   int min_uV;
+   int max_uV;
+   int min_uA;
+   int max_uA;
+};
+
+struct ufs_vreg_info {
+   struct ufs_vreg *vcc;
+   struct ufs_vreg *vccq;
+   struct ufs_vreg *vccq2;
+};
+
 #endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 3e0a14f..1c5290d 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -51,6 +51,88 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct 
device *dev)
return NULL;
 }
 
+#define MAX_PROP_SIZE 32
+static int ufshcd_populate_vreg(struct device *dev, const char *name,
+   struct ufs_vreg **out_vreg)
+{
+   int ret = 0;
+   u32 temp = 0;
+   char prop_name[MAX_PROP_SIZE];
+   struct ufs_vreg *vreg = NULL;
+   struct device_node *np = dev->of_node;
+
+   if (!np) {
+   dev_err(dev, "%s: non DT initialization\n", __func__);
+   goto out;
+   }
+
+   snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name);
+   if (!of_parse_phandle(np, prop_name, 0)) {
+   dev_info(dev, "%s: Unable to find %s regulator, assuming 
enabled\n",
+   __func__, name);
+   goto out;
+   }
+
+   vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+   if (!vreg) {
+   dev_err(dev, "No memory for %s regulator\n", name);
+   goto out;
+   }
+
+   vreg->name = kstrdup(name, GFP_KERNEL);
+
+   snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name);
+   ret = of_property_read_u32_array(np, prop_name, &temp, 1);
+   if (!ret) {
+   vreg->max_uA = temp;
+   } else {
+   dev_err(dev, "%s: unable to find %s err %d\n",
+   __func__, prop_name, ret);
+   goto out_free;
+   }
+
+   vreg->min_uA = 0;
+   if (!strcmp(name, "vcc")) 

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

2013-07-29 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 Processed requests which are completed w/wo error are reported to
  SCSI layer and any pending commands that are not started are aborted
  in the controller and re-queued into scsi mid-layer queue.

o Upon determining fatal error condition the host controller may hang
  forever until a reset is applied. Block SCSI layer for sending new
  requests and apply reset in a separate error handling work.

o SCSI is informed about the expected Unit-Attention 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 
---
 drivers/scsi/ufs/ufshcd.c |  229 -
 drivers/scsi/ufs/ufshcd.h |   10 ++-
 2 files changed, 149 insertions(+), 90 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c577e6e..4dca9b4 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -79,6 +79,14 @@ enum {
UFSHCD_EH_IN_PROGRESS = (1 << 0),
 };
 
+/* 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,
@@ -101,6 +109,8 @@ 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);
+static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag);
 
 /*
  * ufshcd_wait_for_register - wait for register value to change
@@ -1523,9 +1533,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;
 }
@@ -1651,66 +1658,6 @@ static int ufshcd_verify_dev_init(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
  *
@@ -1727,6 +1674,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
@@ -1930,6 +1880,9 @@ ufshcd_transfer_rsp_sta

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

2013-07-29 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 are rebased on:
[PATCH 9/9] drivers/scsi/ufs: don't check resource with devm_ioremap_resource

Changes from v4:
- Addressed comments from Seungwon Jeon on V3
- Updated with proper locking while ufshcd state changes
- Retained LUN reset instead of device reset with DME_END_POINT_RESET
- Removed error handling decisions dependent on OCS value
- Simplified fatal error handling to abort the requests first
  and then carry out reset.
Changes from v3:
- Rebased.
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 |  684 -
 drivers/scsi/ufs/ufshcd.h |   20 +-
 2 files changed, 501 insertions(+), 203 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 V5 3/4] scsi: ufs: Fix device and host reset methods

2013-07-29 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 
---
 drivers/scsi/ufs/ufshcd.c |  240 +++--
 drivers/scsi/ufs/ufshcd.h |2 +
 2 files changed, 189 insertions(+), 53 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index d4ee48d..c577e6e 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -69,9 +69,14 @@ enum {
 
 /* UFSHCD states */
 enum {
-   UFSHCD_STATE_OPERATIONAL,
UFSHCD_STATE_RESET,
UFSHCD_STATE_ERROR,
+   UFSHCD_STATE_OPERATIONAL,
+};
+
+/* UFSHCD error handling flags */
+enum {
+   UFSHCD_EH_IN_PROGRESS = (1 << 0),
 };
 
 /* Interrupt configuration options */
@@ -87,6 +92,16 @@ enum {
INT_AGGR_CONFIG,
 };
 
+#define ufshcd_set_eh_in_progress(h) \
+   (h->eh_flags |= UFSHCD_EH_IN_PROGRESS)
+#define ufshcd_eh_in_progress(h) \
+   (h->eh_flags & UFSHCD_EH_IN_PROGRESS)
+#define ufshcd_clear_eh_in_progress(h) \
+   (h->eh_flags &= ~UFSHCD_EH_IN_PROGRESS)
+
+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
@@ -840,10 +855,25 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *cmd)
 
tag = cmd->request->tag;
 
-   if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   switch (hba->ufshcd_state) {
+   case UFSHCD_STATE_OPERATIONAL:
+   break;
+   case UFSHCD_STATE_RESET:
err = SCSI_MLQUEUE_HOST_BUSY;
-   goto out;
+   goto out_unlock;
+   case UFSHCD_STATE_ERROR:
+   set_host_byte(cmd, DID_ERROR);
+   cmd->scsi_done(cmd);
+   goto out_unlock;
+   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_unlock;
}
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
 
/* acquire the tag to make sure device cmds don't use it */
if (test_and_set_bit_lock(tag, &hba->lrb_in_use)) {
@@ -880,6 +910,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *cmd)
/* issue command to the controller */
spin_lock_irqsave(hba->host->host_lock, flags);
ufshcd_send_command(hba, tag);
+out_unlock:
spin_unlock_irqrestore(hba->host->host_lock, flags);
 out:
return err;
@@ -1495,8 +1526,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;
 }
@@ -2245,8 +2274,12 @@ static void ufshcd_err_handler(struct ufs_hba *hba)
}
return;
 fatal_eh:
-   hba->ufshcd_state = UFSHCD_STATE_ERROR;
-   schedule_work(&hba->feh_workq);
+   /* handle fatal errors only when link is functional */
+   if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) {
+   /* block commands at driver layer until error is handled */
+   hba->ufshcd_state = UFSHCD_STATE_ERROR;
+   schedule_work(&hba->feh_workq);
+   }
 }
 
 /**
@@ -2411,12 +2444,13 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int 
lun_id, int task_id,
 }
 
 /**
- * ufshcd_device_reset - reset device and abort all the pending commands
+ * ufshcd_eh_device_reset_handler - device reset handler registered to
+ *scsi layer.
  * @cmd: SCSI command pointer
  *
  * Returns SUCCESS/FAILED
  */
-static int ufshcd_device_reset(struct scsi_cmnd *cmd)
+static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
 {
struct Scsi_Host *host;
struct ufs_hba *hba;
@@ -2425,6 +2459,7 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd)
int err;
u8 resp = 0xF;
struct ufshcd_lrb *lrbp;
+   unsigned long flags;
 
host = cmd->device->host;
hba = shost_priv(host);
@@ -2433,55 +2468,33 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd)
lrbp = &hba->lrb[tag];
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp);
  

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

2013-07-29 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 
---
 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 d7f3746..d4ee48d 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2485,6 +2485,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)
@@ -2493,7 +2499,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 = 0xF;
struct ufshcd_lrb *lrbp;
 
@@ -2501,33 +2508,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_unlo

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

2013-07-29 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 
---
 drivers/scsi/ufs/ufshcd.c |  173 ++---
 drivers/scsi/ufs/ufshcd.h |8 ++-
 2 files changed, 122 insertions(+), 59 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index b36ca9a..d7f3746 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
 
@@ -183,13 +186,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)
 {
-   return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs);
+   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)
+{
+   clear_bit_unlock(slot, &hba->tm_slots_in_use);
 }
 
 /**
@@ -1700,10 +1725,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;
@@ -1724,19 +1750,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;
 }
 
 /**
@@ -2237,7 +2259,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-29 Thread Sujit Reddy Thumma
On 7/24/2013 7:09 PM, Seungwon Jeon wrote:
> On Wed, July 24, 2013, Sujit Reddy Thumma wrote:
>> On 7/23/2013 1:57 PM, Seungwon Jeon wrote:
>>> On Sat, July 20, 2013, Sujit Reddy Thumma wrote:
>>>> On 7/19/2013 7:27 PM, Seungwon Jeon wrote:
>>>>> On Tue, July 09, 2013, Sujit Reddy Thumma wrote:
>>>>>> 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 
>>>>>> ---
>>>>>> 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) {
>>>>> Lock is no needed for ufshcd_state?
>>> Please check?
>>
>> Yes, it is needed. Thanks for catching this.
>>
>>>
>>>>>
>>>>>> +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);
>>>>>> +  

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

2013-07-29 Thread Sujit Reddy Thumma
On 7/24/2013 7:09 PM, Seungwon Jeon wrote:
> On Wed, July 24, 2013, Sujit Reddy Thumma wrote:
>> On 7/23/2013 2:04 PM, Seungwon Jeon wrote:
>>> On Sat, July 20, 2013, Sujit Reddy Thumma wrote:
>>>> On 7/19/2013 7:28 PM, Seungwon Jeon wrote:
>>>>> On Tue, July 09, 2013, Sujit Reddy Thumma wrote:
>>>>>> 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
>>>>> Attention'i',  typo.
>>>> Okay.
>>>>
>>>>>
>>>>>>  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 
>>>>>> ---
>>>>>> 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

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

2013-07-23 Thread Sujit Reddy Thumma
On 7/23/2013 2:04 PM, Seungwon Jeon wrote:
> On Sat, July 20, 2013, Sujit Reddy Thumma wrote:
>> On 7/19/2013 7:28 PM, Seungwon Jeon wrote:
>>> On Tue, July 09, 2013, Sujit Reddy Thumma wrote:
>>>> 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
>>> Attention'i',  typo.
>> Okay.
>>
>>>
>>>> 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 
>>>> ---
>>>>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 */
>>>>

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

2013-07-23 Thread Sujit Reddy Thumma
On 7/23/2013 1:54 PM, Seungwon Jeon wrote:
> On Sat, July 20, 2013, Sujit Reddy Thumma wrote:
>> On 7/19/2013 7:26 PM, Seungwon Jeon wrote:
>>> On Tue, July 09, 2013 Sujit Reddy Thumma wrote:
>>>> 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 
>>>> ---
>>>>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_TIMEOUT100 /* 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_sl

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

2013-07-23 Thread Sujit Reddy Thumma
On 7/23/2013 1:57 PM, Seungwon Jeon wrote:
> On Sat, July 20, 2013, Sujit Reddy Thumma wrote:
>> On 7/19/2013 7:27 PM, Seungwon Jeon wrote:
>>> On Tue, July 09, 2013, Sujit Reddy Thumma wrote:
>>>> 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 
>>>> ---
>>>>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) {
>>> Lock is no needed for ufshcd_state?
> Please check?

Yes, it is needed. Thanks for catching this.

> 
>>>
>>>> +  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;

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

2013-07-23 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 
---
 drivers/scsi/ufs/ufshcd.c |  174 ++---
 drivers/scsi/ufs/ufshcd.h |8 ++-
 2 files changed, 123 insertions(+), 59 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 1f2caa0..1d7e027 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
 
@@ -183,13 +186,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)
 {
-   return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs);
+   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)
+{
+   clear_bit_unlock(slot, &hba->tm_slots_in_use);
 }
 
 /**
@@ -1700,10 +1725,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;
@@ -1724,19 +1750,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;
 }
 
 /**
@@ -2237,7 +2259,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba

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

2013-07-23 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-Attention 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 
---
 drivers/scsi/ufs/ufshcd.c |  350 +++-
 drivers/scsi/ufs/ufshcd.h |2 +
 drivers/scsi/ufs/ufshci.h |   19 ++-
 3 files changed, 296 insertions(+), 75 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 1a0ceb2..82600d6 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
@@ -1527,9 +1536,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;
 }
@@ -1655,66 +1661,6 @@ static int ufshcd_verify_dev_init(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
  *
@@ -1731,6 +1677,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-&

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

2013-07-23 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 
---
 drivers/scsi/ufs/ufshcd.c |  437 +++--
 drivers/scsi/ufs/ufshcd.h |2 +
 2 files changed, 381 insertions(+), 58 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 3f80396..1a0ceb2 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
@@ -840,9 +862,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 */
@@ -1495,8 +1530,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;
 }
@@ -2212,6 +2245,100 @@ 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)
+{
+   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 */
+   err = ufshcd_wait_for_register(hba, REG_UTP_TASK_REQ_DOOR_BELL,
+   hba->outstanding_tasks, ~hba->outstanding_tasks,
+   1000, 1000);
+
+   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)
+

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

2013-07-23 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 
---
 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 1d7e027..3f80396 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2486,6 +2486,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)
@@ -2494,7 +2500,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;
 
@@ -2502,33 +2509,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_ir

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

2013-07-23 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 V4 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
[PATCH V4 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization
[PATCH V4 1/2] scsi: ufs: Add support for host assisted background operations
[PATCH V4 2/2] scsi: ufs: Add runtime PM support for UFS host controller driver

Changes from v3:
- Rebased.
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 | 1003 -
 drivers/scsi/ufs/ufshcd.h |   12 +-
 drivers/scsi/ufs/ufshci.h |   19 +-
 3 files changed, 841 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 V4 2/2] scsi: ufs: Add runtime PM support for UFS host controller driver

2013-07-23 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 
---
 drivers/scsi/ufs/ufshcd-pci.c|   62 ++
 drivers/scsi/ufs/ufshcd-pltfrm.c |   48 -
 drivers/scsi/ufs/ufshcd.c|8 +
 3 files changed, 111 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
index 48be39a..57ea9dd 100644
--- a/drivers/scsi/ufs/ufshcd-pci.c
+++ b/drivers/scsi/ufs/ufshcd-pci.c
@@ -35,6 +35,7 @@
 
 #include "ufshcd.h"
 #include 
+#include 
 
 #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,45 @@ 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 ufs_hba *hba = dev_get_drvdata(dev);
+
+   if (!hba)
+   return 0;
+
+   return ufshcd_runtime_suspend(hba);
+}
+static int ufshcd_pci_runtime_resume(struct device *dev)
+{
+   struct ufs_hba *hba = dev_get_drvdata(dev);
+
+   if (!hba)
+   return 0;
+
+   return ufshcd_runtime_resume(hba);
+}
+static int ufshcd_pci_runtime_idle(struct device *dev)
+{
+   struct ufs_hba *hba = dev_get_drvdata(dev);
+
+   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 +129,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 +209,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 +225,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 +246,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..cc957fc 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -34,6 +34,7 @@
  */
 
 #include 
+#include 
 
 #include "ufshcd.h"
 
@@ -87,6 +88,40 @@ 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 ufs_hba *hba =  dev_get_drvdata(dev);
+
+   if (!hba)
+   return 0;
+
+   return ufshcd_runtime_suspend(hba);
+}
+static int ufshcd_pltfrm_runtime_resume(struct device *dev)
+{
+   struct ufs_hba *hba =  dev_get_drvdata(dev);
+
+   if (!hba)
+   return 0;
+
+   return ufshcd_runtime_resume(hba);
+}
+static int ufshcd_pltfrm_runtime_idle(struct device *dev)
+{
+   struct ufs_hba *hba =  dev_get_drvdata(dev);
+
+   if (!hba)
+   return 0;
+
+   return ufshcd_runtime_idle(hba);
+}
+#else /* !CONFIG_PM_RUNTIME *

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

2013-07-23 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 
---
 drivers/scsi/ufs/ufs.h|   23 +++
 drivers/scsi/ufs/ufshcd.c |  343 +
 drivers/scsi/ufs/ufshcd.h |   10 ++
 3 files changed, 376 insertions(+), 0 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 50d32f1..bce09a6 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -107,6 +107,28 @@ enum {
 /* Flag idn for Query Requests*/
 enum flag_idn {
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 */
@@ -155,6 +177,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 7b581f7..4267246 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -261,6 +261,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
@@ -1107,6 +1122,77 @@ out_unlock:
 }
 
 /**
+ * ufshcd_query_attr - API function for sending 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 *request;
+   struct ufs_query_res *response;
+   int err;
+
+   BUG_ON(!hba);
+
+   if (!attr_val) {
+   dev_err(hba->dev, "%s: attribute value required for opcode 
0x%x\n",
+   __func__, opcode);
+   err = -EINVAL;
+   goto out;
+   }
+
+   mutex_lock(&hba->dev_cmd.lock);
+   request = &hba->dev_cmd.query.request;
+   response = &hba->dev_cmd.query.response;
+   memset(request, 0, sizeof(struct ufs_query_req));
+   memset(response, 0, sizeof(struct ufs_query_res));
+
+   switch (opcode) {
+   case UPIU_QUERY_OPCODE_WRITE_ATTR:
+   request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST;
+   request->upiu_req.value = *attr_val;
+ 

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

2013-07-23 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 
Signed-off-by: Dolev Raviv 
---
 drivers/scsi/ufs/ufs.h|   40 +++-
 drivers/scsi/ufs/ufshcd.c |  585 +
 drivers/scsi/ufs/ufshcd.h |   29 ++-
 3 files changed, 539 insertions(+), 115 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 139bc06..51b5e3f 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -36,10 +36,13 @@
 #ifndef _UFS_H
 #define _UFS_H
 
+#include 
+#include 
+
 #define MAX_CDB_SIZE   16
 
 #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
-   ((byte3 << 24) | (byte2 << 16) |\
+   cpu_to_be32((byte3 << 24) | (byte2 << 16) |\
 (byte1 << 8) | (byte0))
 
 /*
@@ -73,6 +76,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 +114,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 +148,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 +181,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..ed20156 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,40 @@ 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 -ETIMEDOUT on error, zero on success
+ */
+static int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
+   u32 val, unsigned long interval_us, unsigned long timeout_ms)
+{
+   int 

[PATCH V4 0/2] scsi: ufs: Add support to control UFS device background operations

2013-07-23 Thread Sujit Reddy Thumma
Add host assisted background operations for UFS device and runtime PM
helpers for ufshcd platform and pci glue drivers. The background operations
are disabled during runtime resume and enabled when the device is idle and
runtime suspended.

These patches depends on:
[PATCH V4 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
[PATCH V4 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization

Changes from v3:
- Addressed some of the comments by Seungwon Jeon on runtime PM.
Changes from v2:
- Enable auto bkops by default explicitly during bootup so that we
  may not assume it as enabled after reset.
- Enable runtime PM support for contexts that are not part of SCSI,
  so that the host is not suspended while running other contexts
  like bkops exception handling.
Changes from v1:
- Minor cleanup and rebase
- Forced enable of auto bkops during initialization to make sure device
  and driver state are matched.


Sujit Reddy Thumma (2):
  scsi: ufs: Add support for host assisted background operations
  scsi: ufs: Add runtime PM support for UFS host controller driver

 drivers/scsi/ufs/ufs.h   |   23 +++
 drivers/scsi/ufs/ufshcd-pci.c|   62 ++-
 drivers/scsi/ufs/ufshcd-pltfrm.c |   48 +-
 drivers/scsi/ufs/ufshcd.c|  351 ++
 drivers/scsi/ufs/ufshcd.h|   10 +
 5 files changed, 487 insertions(+), 7 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 V4 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization

2013-07-23 Thread Sujit Reddy Thumma
From: Dolev Raviv 

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 
Signed-off-by: Sujit Reddy Thumma 
---
 drivers/scsi/ufs/ufs.h|   96 +--
 drivers/scsi/ufs/ufshcd.c |  238 -
 drivers/scsi/ufs/ufshcd.h |   20 
 drivers/scsi/ufs/ufshci.h |2 +-
 4 files changed, 345 insertions(+), 11 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 51b5e3f..50d32f1 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -40,6 +40,10 @@
 #include 
 
 #define MAX_CDB_SIZE   16
+#define GENERAL_UPIU_REQUEST_SIZE 32
+#define QUERY_DESC_MAX_SIZE   256
+#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) |\
@@ -65,7 +69,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 */
@@ -94,8 +98,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,
@@ -107,6 +122,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,
@@ -121,9 +151,10 @@ enum {
 #define UPIU_RSP_CODE_OFFSET   8
 
 enum {
-   MASK_SCSI_STATUS= 0xFF,
-   MASK_TASK_RESPONSE  = 0xFF00,
-   MASK_RSP_UPIU_RESULT= 0x,
+   MASK_SCSI_STATUS= 0xFF,
+   MASK_TASK_RESPONSE  = 0xFF00,
+   MASK_RSP_UPIU_RESULT= 0x,
+   MASK_QUERY_DATA_SEG_LEN = 0x,
 };
 
 /* Task management service response */
@@ -157,13 +188,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;
+   };
 };
 
 /**
@@ -184,10 +242,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;
+

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

2013-07-23 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 v3:
- Addressed some of the comments by Seungwon Jeon.
  o Changed return value for ufshcd_wait_for_register().
  o Function name changes to ufshcd_verify_dev_init().
  o Bootup allocation for query structures.
  o Remove overloading of flag_res while polling in fDeviceInit.
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|  132 +++-
 drivers/scsi/ufs/ufshcd.c |  817 +++--
 drivers/scsi/ufs/ufshcd.h |   49 +++-
 drivers/scsi/ufs/ufshci.h |2 +-
 4 files changed, 879 insertions(+), 121 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


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

2013-07-19 Thread Sujit Reddy Thumma



I'm not sure that BKOPS with runtime-pm associates.
Do you think it's helpful for power management?
How about hibernation scheme for runtime-pm?
I'm testing and I can introduce soon.


Well, I am thinking on following approach when we introduce
power management.

ufshcd_runtime_suspend() {
if (bkops_status >= NON_CRITICAL) { /* 0x1 */
ufshcd_enable_auto_bkops();
hibernate(); /* only the link and the device
should be able to cary out bkops */
} else {
hibernate(); /* Link and the device for more savings */
}
}

Let me know if this is okay.

I still consider whether BKOPS is proper behavior with runtime-pm or not.


The BKOPS is something that host allows the card to carry out
when the host knows it is idle and not expecting back to back requests.
Runtime PM idle is the only way to know whether the device is
idle (unless we want to reinvent the wheel to detect the idleness of
host and trigger bkops). There was a discussion on this in MMC mailing
list as well, and folks have agreed to move idle time bkops to runtime
PM (http://thread.gmane.org/gmane.linux.kernel.mmc/19444/)

It looks like different.
eMMC cannot execute BKOPS itself unlike UFS.
That's the way eMMC's host should trigger the BKOPS manually.



I guess it is not much of a difference for UFS as far as we concern
only about idle time bkops. In UFS one can still disallow the device to
not carry out BKOPS and hence the case where UFS device also cannot
execute BKOPS itself and a idle timer is needed to allow BKOPS
sporadically so that device doesn't go into URGENT_BKOPS state.




How about this scenario? It seems more simple.

> > If we concern a response latency of transfer requests, BKOPS can be 
disabled by default.

And then BKOPS can be enabled whenever device requests in some exception.
If you have any idea, let me know.


Exceptions are raised only when the device is in critical need for
bkops. Also the spec. recommends, host should ensure that the device
doesn't go into such states.

With your suggestion, when we disable bkops, the exception is raised and
we enable bkops after which there is no way to disable it again?

Yes, it's difficult to find proper time.
Maybe, BKOPS can be disabled when request comes up.


In cases where there are back-to-back heavy data write requests then it
is not proper to enable/disable BKOPS as soon as the new request comes
up. It may happen that the card will then move from PERFORMANCE_IMPACT
state to CRITICAL and ultimately start failing the requests. The point
is that we should never get into state where we need URGENT_BKOPS.

--
Regards,
Sujit
--
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


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

2013-07-19 Thread Sujit Reddy Thumma
On 7/19/2013 7:27 PM, Seungwon Jeon wrote:
> On Tue, July 09, 2013, Sujit Reddy Thumma wrote:
>> 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 
>> ---
>>   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) {
> Lock is no needed for 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
>> +

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

2013-07-19 Thread Sujit Reddy Thumma
On 7/19/2013 7:26 PM, Seungwon Jeon wrote:
> On Tue, July 09, 2013 Sujit Reddy Thumma wrote:
>> 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 
>> ---
>>   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_upi

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

2013-07-19 Thread Sujit Reddy Thumma
On 7/19/2013 7:28 PM, Seungwon Jeon wrote:
> On Tue, July 09, 2013, Sujit Reddy Thumma wrote:
>> 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
> Attention'i',  typo.
Okay.

> 
>>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 
>> ---
>>   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 managem

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

2013-07-19 Thread Sujit Reddy Thumma
On 7/19/2013 7:26 PM, Seungwon Jeon wrote:
> On Tue, July 09, 2013, Sujit Reddy Thumma wrote:
>> 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.
> It's interesting.
> I wonder when we can meet this situation.
> Is it possible if SCSI mid layer should send abort command as soon as the 
> transfer command is issued?
> AFAIK abort command is followed if one command has timed out.
> That means command have been already issued and no response?
> If you had some problem, could you share?

You are right. This is a very rare case and probably don't happen at all
until and unless the SCSI error handling is changed. We found it just by
static analysis. Probably, at some point I may push a patch that tries
to reduce the read latencies while aborting a large write transfer when
I come across a UFS device that has command per LU as 1 :-)

I guess this is good to have change. The path chosen is according to
SCSI SAM-5 architecture specification, so I don't expect any issues
here.

> 
>> - If the device recieves abort command first then it returns success
> receives
> 
>>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 
>> ---
>>   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

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

2013-07-19 Thread Sujit Reddy Thumma

On 7/19/2013 7:17 PM, Seungwon Jeon wrote:

On Thu, July 18, 2013, Sujit Reddy Thumma wrote:

+ * ufshcd_wait_for_register - wait for register value to change
+ * @hba - per-adapter interface
+ * @reg - mmio register offset
+ * @mask - mask to apply to read register value
+ * @val - wait condition
+ * @interval_us - polling interval in microsecs
+ * @timeout_ms - timeout in millisecs
+ *
+ * Returns final register value after iteration
+ */
+static u32 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
+   u32 val, unsigned long interval_us, unsigned long timeout_ms)

I feel like this function's role is to wait for clearing register (specially, 
doorbells).
If you really don't intend to apply other all register, I think it would better 
to change the

function name.

ex> ufshcd_wait_for_clear_doorbell or ufshcd_wait_for_clear_reg?


Although, this API is introduced in this patch and used only for
clearing the doorbell, I have intentionally made it generic to avoid
duplication of wait condition code in future (not just clearing but
also for setting a bit). For example, when we write to HCE.ENABLE we
wait for it turn to 1.



And if you like it, it could be more simple like below

static bool ufshcd_wait_for_clear_reg(struct ufs_hba *hba, u32 reg, u32 mask,
unsigned long interval_us,
unsigned int timeout_ms)
{
   unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);


Jiffies on 100Hz systems have minimum granularity of 10ms. So we really
wait for more 10ms even though the timeout_ms < 10ms.

Yes. Real timeout depends on system.
But normally actual wait will be maintained until 'ufshcd_readl' is done.
Timeout is valid for failure case.  If we don't need to apply a strict timeout 
value, it's not bad.


Hmm.. make sense. Will take care in the next patchset.






   /* wakeup within 50us of expiry */
   const unsigned int expiry = 50;

   while (ufshcd_readl(hba, reg) & mask) {
   usleep_range(interval_us, interval_us + expiry);
   if (time_after(jiffies, timeout)) {
   if (ufshcd_readl(hba, reg) & mask)
   return false;


I really want the caller to decide on what to do after the timeout.
This helps in error handling cases where we try to clear off the entire
door-bell register but only a few of the bits are cleared after timeout.

I don't know what we can do with the report of the partial success on clearing 
bit.
It's just failure case. Isn't enough with false/true?


The point is, if a bit can't be cleared it can be regarded as a
permanent failure (only for that request), otherwise, it can be
retried with the same tag value.






   else
   return true;
   }
   }

   return true;
}

+{
+   u32 tmp;
+   ktime_t start;
+   unsigned long diff;
+
+   tmp = ufshcd_readl(hba, reg);
+
+   if ((val & mask) != val) {
+   dev_err(hba->dev, "%s: Invalid wait condition 0x%x\n",
+   __func__, val);
+   goto out;
+   }
+
+   start = ktime_get();
+   while ((tmp & mask) != val) {
+   /* wakeup within 50us of expiry */
+   usleep_range(interval_us, interval_us + 50);
+   tmp = ufshcd_readl(hba, reg);
+   diff = ktime_to_ms(ktime_sub(ktime_get(), start));
+   if (diff > timeout_ms) {
+   tmp = ufshcd_readl(hba, reg);
+   break;
+   }
+   }
+out:
+   return tmp;
+}
+

..

+static int
+ufshcd_clear_cmd(struct ufs_hba *hba, int tag)
+{
+   int err = 0;
+   unsigned long flags;
+   u32 reg;
+   u32 mask = 1 << tag;
+
+   /* clear outstanding transaction before retry */
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   ufshcd_utrl_clear(hba, tag);
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+   /*
+* wait for for h/w to clear corresponding bit in door-bell.
+* max. wait is 1 sec.
+*/
+   reg = ufshcd_wait_for_register(hba,
+   REG_UTP_TRANSFER_REQ_DOOR_BELL,
+   mask, 0, 1000, 1000);

4th argument should be (~mask) instead of '0', right?


True, but not really for this implementation. It breaks the check in
in wait_for_register -
if ((val & mask) != val)
  dev_err(...);

Hmm, it seems complicated to use.
Ok. Is right the following about val as 4th argument?
- clear: val  should be '0' regardless corresponding bit.
- set: val should be same with mask.
If the related comment is added, it will be helpful.


Thinking again it looks l

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

2013-07-17 Thread Sujit Reddy Thumma




+ * ufshcd_wait_for_register - wait for register value to change
+ * @hba - per-adapter interface
+ * @reg - mmio register offset
+ * @mask - mask to apply to read register value
+ * @val - wait condition
+ * @interval_us - polling interval in microsecs
+ * @timeout_ms - timeout in millisecs
+ *
+ * Returns final register value after iteration
+ */
+static u32 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
+   u32 val, unsigned long interval_us, unsigned long timeout_ms)

I feel like this function's role is to wait for clearing register (specially, 
doorbells).
If you really don't intend to apply other all register, I think it would better 
to change the

function name.

ex> ufshcd_wait_for_clear_doorbell or ufshcd_wait_for_clear_reg?


Although, this API is introduced in this patch and used only for
clearing the doorbell, I have intentionally made it generic to avoid
duplication of wait condition code in future (not just clearing but
also for setting a bit). For example, when we write to HCE.ENABLE we
wait for it turn to 1.



And if you like it, it could be more simple like below

static bool ufshcd_wait_for_clear_reg(struct ufs_hba *hba, u32 reg, u32 mask,
   unsigned long interval_us,
   unsigned int timeout_ms)
{
  unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);


Jiffies on 100Hz systems have minimum granularity of 10ms. So we really
wait for more 10ms even though the timeout_ms < 10ms.

Yes. Real timeout depends on system.
But normally actual wait will be maintained until 'ufshcd_readl' is done.
Timeout is valid for failure case.  If we don't need to apply a strict timeout 
value, it's not bad.


Hmm.. make sense. Will take care in the next patchset.






  /* wakeup within 50us of expiry */
  const unsigned int expiry = 50;

  while (ufshcd_readl(hba, reg) & mask) {
  usleep_range(interval_us, interval_us + expiry);
  if (time_after(jiffies, timeout)) {
  if (ufshcd_readl(hba, reg) & mask)
  return false;


I really want the caller to decide on what to do after the timeout.
This helps in error handling cases where we try to clear off the entire
door-bell register but only a few of the bits are cleared after timeout.

I don't know what we can do with the report of the partial success on clearing 
bit.
It's just failure case. Isn't enough with false/true?


The point is, if a bit can't be cleared it can be regarded as a
permanent failure (only for that request), otherwise, it can be
retried with the same tag value.






  else
  return true;
  }
  }

  return true;
}

+{
+   u32 tmp;
+   ktime_t start;
+   unsigned long diff;
+
+   tmp = ufshcd_readl(hba, reg);
+
+   if ((val & mask) != val) {
+   dev_err(hba->dev, "%s: Invalid wait condition 0x%x\n",
+   __func__, val);
+   goto out;
+   }
+
+   start = ktime_get();
+   while ((tmp & mask) != val) {
+   /* wakeup within 50us of expiry */
+   usleep_range(interval_us, interval_us + 50);
+   tmp = ufshcd_readl(hba, reg);
+   diff = ktime_to_ms(ktime_sub(ktime_get(), start));
+   if (diff > timeout_ms) {
+   tmp = ufshcd_readl(hba, reg);
+   break;
+   }
+   }
+out:
+   return tmp;
+}
+

..

+static int
+ufshcd_clear_cmd(struct ufs_hba *hba, int tag)
+{
+   int err = 0;
+   unsigned long flags;
+   u32 reg;
+   u32 mask = 1 << tag;
+
+   /* clear outstanding transaction before retry */
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   ufshcd_utrl_clear(hba, tag);
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+   /*
+* wait for for h/w to clear corresponding bit in door-bell.
+* max. wait is 1 sec.
+*/
+   reg = ufshcd_wait_for_register(hba,
+   REG_UTP_TRANSFER_REQ_DOOR_BELL,
+   mask, 0, 1000, 1000);

4th argument should be (~mask) instead of '0', right?


True, but not really for this implementation. It breaks the check in
in wait_for_register -
if ((val & mask) != val)
 dev_err(...);

Hmm, it seems complicated to use.
Ok. Is right the following about val as 4th argument?
- clear: val  should be '0' regardless corresponding bit.
- set: val should be same with mask.
If the related comment is added, it will be helpful.


Thinking again it looks like it is complicated. How about changing
the check to -

val = val & mask; /* ignore the bits we don't intend to wait on */
while (ufshcd_readl() & mask != val) {
 sleep
}

Usage will be ~mask for clearing the bits, mask for setting the bits
in

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

2013-07-17 Thread Sujit Reddy Thumma



> >I'm not sure that BKOPS with runtime-pm associates.
> >Do you think it's helpful for power management?
> >How about hibernation scheme for runtime-pm?
> >I'm testing and I can introduce soon.

>
>Well, I am thinking on following approach when we introduce
>power management.
>
>ufshcd_runtime_suspend() {
>if (bkops_status >= NON_CRITICAL) { /* 0x1 */
>ufshcd_enable_auto_bkops();
>hibernate(); /* only the link and the device
>should be able to cary out bkops */
>} else {
>hibernate(); /* Link and the device for more savings */
>}
>}
>
>Let me know if this is okay.

I still consider whether BKOPS is proper behavior with runtime-pm or not.


The BKOPS is something that host allows the card to carry out
when the host knows it is idle and not expecting back to back requests.
Runtime PM idle is the only way to know whether the device is
idle (unless we want to reinvent the wheel to detect the idleness of
host and trigger bkops). There was a discussion on this in MMC mailing
list as well, and folks have agreed to move idle time bkops to runtime
PM (http://thread.gmane.org/gmane.linux.kernel.mmc/19444/)


How about this scenario? It seems more simple.
If we concern a response latency of transfer requests, BKOPS can be disabled by 
default.
And then BKOPS can be enabled whenever device requests in some exception.
If you have any idea, let me know.


Exceptions are raised only when the device is in critical need for
bkops. Also the spec. recommends, host should ensure that the device
doesn't go into such states.

With your suggestion, when we disable bkops, the exception is raised and 
we enable bkops after which there is no way to disable it again?


--
Regards,
Sujit
--
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


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

2013-07-11 Thread Sujit Reddy Thumma

On 7/10/2013 7:01 PM, Seungwon Jeon wrote:

On Tue, July 09, 2013, Sujit Reddy Thumma wrote:

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 
---
  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 
+#include 

  #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);

Can be replaced:
struct ufs_hba *hba = dev_get_drvdata(dev);


Yes.




+
+   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);

Same as above.


Okay.





+
+   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);

Same as above.


Okay.



+
+   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 
+#include 

  #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);

Same as above.

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

2013-07-11 Thread Sujit Reddy Thumma

On 7/10/2013 7:01 PM, Seungwon Jeon wrote:

I'm not sure that BKOPS with runtime-pm associates.
Do you think it's helpful for power management?
How about hibernation scheme for runtime-pm?
I'm testing and I can introduce soon.


Well, I am thinking on following approach when we introduce
power management.

ufshcd_runtime_suspend() {
if (bkops_status >= NON_CRITICAL) { /* 0x1 */
ufshcd_enable_auto_bkops();
hibernate(); /* only the link and the device
should be able to cary out bkops */
} else {
hibernate(); /* Link and the device for more savings */
}
}

Let me know if this is okay.



On Tue, July 09, 2013, Sujit Reddy Thumma wrote:

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 
---
  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 *resp

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

2013-07-11 Thread Sujit Reddy Thumma
On 7/10/2013 6:58 PM, Seungwon Jeon wrote:
> On Tue, July 09, 2013, Sujit Reddy Thumma wrote:
>> 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 
>> Signed-off-by: Dolev Raviv 
>> ---
>>   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 
>> +#include 
>> +
>>   #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_OFFSET28
>> +
>> +/* Offset of the response code in the UPIU header */
>> +#define UPIU_RSP_CODE_OFFSET8
>> +
>>   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;
>

[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 
---
 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 
+#include 
 
 #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 
+#include 
 
 #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 ufs_hba

[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 
---
 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 */
+

[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 
---
 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_irqres

[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 
---
 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

[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 
---
 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_re

[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 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 
---
 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_ir

[PATCH V3 0/2] scsi: ufs: Add support to control UFS device background operations

2013-07-09 Thread Sujit Reddy Thumma
Add host assisted background operations for UFS device and runtime PM
helpers for ufshcd platform and pci glue drivers. The background operations
are disabled during runtime resume and enabled when the device is idle and
runtime suspended.

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

Changes from v2:
- Enable auto bkops by default explicitly during bootup so that we
  may not assume it as enabled after reset.
- Enable runtime PM support for contexts that are not part of SCSI,
  so that the host is not suspended while running other contexts
  like bkops exception handling.
Changes from v1:
- Minor cleanup and rebase
- Forced enable of auto bkops during initialization to make sure device
  and driver state are matched.

Sujit Reddy Thumma (2):
  scsi: ufs: Add support for host assisted background operations
  scsi: ufs: Add runtime PM support for UFS host controller driver

 drivers/scsi/ufs/ufs.h   |   25 +++-
 drivers/scsi/ufs/ufshcd-pci.c|   65 +++-
 drivers/scsi/ufs/ufshcd-pltfrm.c |   51 ++-
 drivers/scsi/ufs/ufshcd.c|  346 ++
 drivers/scsi/ufs/ufshcd.h|   10 +
 5 files changed, 489 insertions(+), 8 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 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization

2013-07-09 Thread Sujit Reddy Thumma
From: Dolev Raviv 

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 
Signed-off-by: Sujit Reddy Thumma 
---
 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 +283,24 @@ struct utp_upiu_task_rsp {
u3

[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 
Signed-off-by: Dolev Raviv 
---
 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 
+#include 
+
 #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 register value af

[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


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

2013-07-04 Thread Sujit Reddy Thumma

On 7/3/2013 10:22 PM, Santosh Y wrote:

+
+/**
+ * ufshcd_fatal_err_handler - handle fatal errors
+ * @work: pointer to work structure
   */
  static void ufshcd_fatal_err_handler(struct work_struct *work)
  {
 struct ufs_hba *hba;
+   unsigned long flags;
+   u32 err_xfer = 0;
+   u32 err_tm = 0;
+   int err;
+
 hba = container_of(work, struct ufs_hba, feh_workq);

-   /* check if reset is already in progress */
-   if (hba->ufshcd_state != UFSHCD_STATE_RESET)
-   ufshcd_do_reset(hba);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   if (hba->ufshcd_state == UFSHCD_STATE_RESET) {
+   /* complete processed requests and exit */
+   ufshcd_transfer_req_compl(hba);
+   ufshcd_tmc_handler(hba);
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   return;


I could not go through this patch yet, please check if it needs to
wait here until the state is 'operational' or 'error'.
The 'reset' state might be due to the device reset also.


As of now reset is scheduled only in two contexts -
1) Fatal error handling
2) SCSI error handling

If scsi error handling is in progress it changes the state to
UFSHCD_STATE_RESET. If fatal error interrupt is raised it checks
whether the state is operational or not before scheduling the work.
So in any case, there is no race between fatal error handler
and the scsi error handler and hence there is no need any wait here.

static void ufshcd_err_handler(struct ufs_hba *hba)
{
...

 fatal_eh:
-   hba->ufshcd_state = UFSHCD_STATE_ERROR;
-   schedule_work(&hba->feh_workq);
+   /* handle fatal errors only when link is functional */
+   if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) {
+   /* block commands from midlayer */
+   scsi_block_requests(hba->host);
+
+   /* block commands at driver layer until error is handled */
+   hba->ufshcd_state = UFSHCD_STATE_ERROR;
+   schedule_work(&hba->feh_workq);
+   }
 }





+   }
+
+   hba->ufshcd_state = UFSHCD_STATE_RESET;
+   ufshcd_error_autopsy_transfer_req(hba, &err_xfer);
+   ufshcd_error_autopsy_task_req(hba, &err_tm);
+





--
Regards,
Sujit
--
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


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

2013-07-03 Thread Sujit Reddy Thumma

On 7/3/2013 11:19 AM, Santosh Y wrote:

+
>+/**
>+ * ufshcd_eh_device_reset_handler - device reset handler registered to
>+ *scsi layer.
>+ * @cmd - SCSI command pointer
>+ *
>+ * Returns SUCCESS/FAILED
>+ */
>+static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
>+{
>+   struct ufs_hba *hba;
>+   int err;
>+   unsigned long flags;
>+
>+   hba = shost_priv(cmd->device->host);
>+
>+   spin_lock_irqsave(hba->host->host_lock, flags);
>+   if (hba->ufshcd_state == UFSHCD_STATE_RESET) {
>+   dev_warn(hba->dev, "%s: reset in progress\n", __func__);
>+   err = SUCCESS;
>+   spin_unlock_irqrestore(hba->host->host_lock, flags);
>+   goto out;

It is better to wait here until the state changes to 'operational' or
'error' before returning success.


Okay. Sounds good.

--
Regards,
Sujit
--
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


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

2013-07-03 Thread Sujit Reddy Thumma

On 7/3/2013 9:53 PM, Santosh Y wrote:

On Wed, Jul 3, 2013 at 9:22 PM, Sujit Reddy Thumma
 wrote:

On 7/2/2013 9:21 PM, Santosh Y wrote:


On Fri, Jun 28, 2013 at 5:02 PM, Sujit Reddy Thumma
 wrote:


On 6/27/2013 4:49 PM, Santosh Y wrote:




+   spin_lock_irqsave(host->host_lock, flags);
  task_req_descp = hba->utmrdl_base_addr;
  task_req_descp += free_slot;

@@ -2353,38 +2387,39 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba,
  (struct utp_upiu_task_req *)
task_req_descp->task_req_upiu;
  task_req_upiup->header.dword_0 =
  UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
- lrbp->lun,
lrbp->task_tag);
+   lun_id, free_slot);



Actually it still doesn't fix the problem. The*task tag*  used here

should be unique across the SCSI/Query and Task Managment UPIUs.




I am sorry, I didn't get that. Why should it be unique across the
SCSI/Query? For example, if a machine supports 32 request slots and 8
task
management slots, then the task management command tag can be anything
out
of 8 slots.



The spec(ufs 1.1) has the requirement under  '10.5.2 Basic Header
Format'->'Task Tag'.
Couple of devices I came across had similar behavior. The tracking of
UPIUs --even belonging to a separate group-- seemed to be based on the
'task tag' value rather than 'type of UPIU'->'task tag'.



Thanks for the clarification. So to make the task tag unique we should do
something like below:

@@ -2940,9 +2941,10 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba,
int lun_id, int task_id,
 /* Configure task request UPIU */
 task_req_upiup =

 (struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
+   task_tag = hba->nutrs + free_slot;


Yes, this was exactly my internal fix at the time :-)


Okay. I will update the patch with this. Thanks.





 task_req_upiup->header.dword_0 =
 UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
-   lun_id, free_slot);
+   lun_id, task_tag);

Will this work for the devices you came across?




--
Regards,
Sujit
--
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


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

2013-07-03 Thread Sujit Reddy Thumma

On 7/2/2013 9:21 PM, Santosh Y wrote:

On Fri, Jun 28, 2013 at 5:02 PM, Sujit Reddy Thumma
 wrote:

On 6/27/2013 4:49 PM, Santosh Y wrote:



+   spin_lock_irqsave(host->host_lock, flags);
 task_req_descp = hba->utmrdl_base_addr;
 task_req_descp += free_slot;

@@ -2353,38 +2387,39 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba,
 (struct utp_upiu_task_req *)
task_req_descp->task_req_upiu;
 task_req_upiup->header.dword_0 =
 UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
- lrbp->lun,
lrbp->task_tag);
+   lun_id, free_slot);


Actually it still doesn't fix the problem. The*task tag*  used here

should be unique across the SCSI/Query and Task Managment UPIUs.



I am sorry, I didn't get that. Why should it be unique across the
SCSI/Query? For example, if a machine supports 32 request slots and 8 task
management slots, then the task management command tag can be anything out
of 8 slots.



The spec(ufs 1.1) has the requirement under  '10.5.2 Basic Header
Format'->'Task Tag'.
Couple of devices I came across had similar behavior. The tracking of
UPIUs --even belonging to a separate group-- seemed to be based on the
'task tag' value rather than 'type of UPIU'->'task tag'.


Thanks for the clarification. So to make the task tag unique we should 
do something like below:


@@ -2940,9 +2941,10 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba 
*hba, int lun_id, int task_id,

/* Configure task request UPIU */
task_req_upiup =
(struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
+   task_tag = hba->nutrs + free_slot;
task_req_upiup->header.dword_0 =
UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
-   lun_id, free_slot);
+   lun_id, task_tag);

Will this work for the devices you came across?


--
Regards,
Sujit
--
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


Re: [PATCH] block: Fix possible sleep in invalid context

2013-07-01 Thread Sujit Reddy Thumma

On 7/2/2013 8:34 AM, Aaron Lu wrote:

Fix this by releasing spin_lock_irq() before calling
>pm_runtime_autosuspend() in blk_post_runtime_resume().

Hi Sujit,

Thanks for testing out block layer runtime PM!

As for the problem here, it is already fixed by:

commit c60855cdb976c632b3bf8922eeab8a0e78edfc04
Author: Aaron Lu
Date:   Fri May 17 15:47:20 2013 +0800

 blkpm: avoid sleep when holding queue lock


Thanks Aaron. I see that is merged in 3.10-rc6.
Please ignore this patch.

--
Regards,
Sujit
--
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] block: Fix possible sleep in invalid context

2013-07-01 Thread Sujit Reddy Thumma
When block runtime PM is enabled following warning is seen
while resuming the device.

BUG: sleeping function called from invalid context at
.../drivers/base/power/runtime.c:923
in_atomic(): 1, irqs_disabled(): 128, pid: 12, name: kworker/0:1
[] (unwind_backtrace+0x0/0x120) from
[] (__pm_runtime_suspend+0x34/0xa0) from
[] (blk_post_runtime_resume+0x4c/0x5c) from
[] (scsi_runtime_resume+0x90/0xb4) from
[] (__rpm_callback+0x30/0x58) from
[] (rpm_callback+0x18/0x28) from
[] (rpm_resume+0x3dc/0x540) from
[] (pm_runtime_work+0x8c/0x98) from
[] (process_one_work+0x238/0x3e4) from
[] (worker_thread+0x1ac/0x2ac) from
[] (kthread+0x88/0x94) from
[] (kernel_thread_exit+0x0/0x8)

Fix this by releasing spin_lock_irq() before calling
pm_runtime_autosuspend() in blk_post_runtime_resume().

Signed-off-by: Sujit Reddy Thumma 
Cc: sta...@vger.kernel.org
---
 block/blk-core.c |6 --
 1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/block/blk-core.c b/block/blk-core.c
index 33c33bc..2456116 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -3159,16 +3159,18 @@ EXPORT_SYMBOL(blk_pre_runtime_resume);
  */
 void blk_post_runtime_resume(struct request_queue *q, int err)
 {
-   spin_lock_irq(q->queue_lock);
if (!err) {
+   spin_lock_irq(q->queue_lock);
q->rpm_status = RPM_ACTIVE;
__blk_run_queue(q);
pm_runtime_mark_last_busy(q->dev);
+   spin_unlock_irq(q->queue_lock);
pm_runtime_autosuspend(q->dev);
} else {
+   spin_lock_irq(q->queue_lock);
q->rpm_status = RPM_SUSPENDED;
+   spin_unlock_irq(q->queue_lock);
}
-   spin_unlock_irq(q->queue_lock);
 }
 EXPORT_SYMBOL(blk_post_runtime_resume);
 #endif
-- 
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 V2 1/4] scsi: ufs: Fix broken task management command implementation

2013-06-28 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 tag used should
 be the free slot of TM command and not the task tag of the
 command which the TM command is trying to manage.
Fix: Use free slot 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 
---
 drivers/scsi/ufs/ufshcd.c |  175 ++---
 drivers/scsi/ufs/ufshcd.h |8 ++-
 2 files changed, 124 insertions(+), 59 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index ac323a8..4781b00 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);
 }
 
 /**
@@ -1809,10 +1834,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;
@@ -1833,19 +1859,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;
 }
 
 /**
@@ -2325,7 +2347,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba)
 
tm_doorb

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

2013-06-28 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 
---
 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 4781b00..6bfe927 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2575,6 +2575,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)
@@ -2583,7 +2589,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;
 
@@ -2591,33 +2598,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_ir

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

2013-06-28 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 
---
 drivers/scsi/ufs/ufshcd.c |  357 +++--
 drivers/scsi/ufs/ufshcd.h |2 +
 drivers/scsi/ufs/ufshci.h |   19 ++-
 3 files changed, 301 insertions(+), 77 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2829a42..3788518 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
@@ -1636,9 +1645,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;
 }
@@ -1764,66 +1770,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
  *
@@ -1840,6 +1786,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 */
+

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

2013-06-28 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 
---
 drivers/scsi/ufs/ufshcd.c |  446 +++--
 drivers/scsi/ufs/ufshcd.h |2 +
 2 files changed, 393 insertions(+), 55 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 6bfe927..2829a42 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
@@ -883,9 +905,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 */
@@ -1604,8 +1639,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;
 }
@@ -2302,6 +2335,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_irqres

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

2013-06-28 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 01/10] scsi: ufs: wrap the i/o access operations
[PATCH 02/10] scsi: ufs: amend interrupt configuration
[PATCH 03/10] scsi: ufs: remove version check before IS reg clear
[PATCH 04/10] scsi: ufs: rework link start-up process
[PATCH 05/10] scsi: ufs: Fix the response UPIU length setting
[PATCH 06/10] scsi: ufs: use devres functions for ufshcd
[PATCH 07/10] ufshcd-pltfrm: add missing empty slot in ufs_of_match[]
[PATCH 08/10] ufs: fix register address in UIC error interrupt handling
[PATCH 09/10] ufshcd-pltfrm: remove unnecessary dma_set_coherent_mask() call
[PATCH 10/10] ufs: fix DMA mask setting
[PATCH V2 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
[PATCH V2 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization
[PATCH V2 1/2] scsi: ufs: Add support for host assisted background operations
[PATCH V2 2/2] scsi: ufs: Add runtime PM helpers for UFS host driver

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 | 1020 -
 drivers/scsi/ufs/ufshcd.h |   12 +-
 drivers/scsi/ufs/ufshci.h |   19 +-
 3 files changed, 859 insertions(+), 192 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 V2 0/2] scsi: ufs: Add support to control UFS device background operations

2013-06-28 Thread Sujit Reddy Thumma
Add host assisted background operations for UFS device and runtime PM
helpers for ufshcd platform and pci glue drivers. The background operations
are disabled during runtime resume and enabled when the device is idle and
runtime suspended.

These patches depends on:
[PATCH 01/10] scsi: ufs: wrap the i/o access operations
[PATCH 02/10] scsi: ufs: amend interrupt configuration
[PATCH 03/10] scsi: ufs: remove version check before IS reg clear
[PATCH 04/10] scsi: ufs: rework link start-up process
[PATCH 05/10] scsi: ufs: Fix the response UPIU length setting
[PATCH 06/10] scsi: ufs: use devres functions for ufshcd
[PATCH 07/10] ufshcd-pltfrm: add missing empty slot in ufs_of_match[]
[PATCH 08/10] ufs: fix register address in UIC error interrupt handling
[PATCH 09/10] ufshcd-pltfrm: remove unnecessary dma_set_coherent_mask() call
[PATCH 10/10] ufs: fix DMA mask setting
[PATCH V2 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
[PATCH V2 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization

Changes from v1:
- Minor cleanup and rebase
- Forced enable of auto bkops during initialization to make sure device
  and driver state are matched.

Sujit Reddy Thumma (2):
  scsi: ufs: Add support for host assisted background operations
  scsi: ufs: Add runtime PM helpers for UFS host driver

 drivers/scsi/ufs/ufs.h   |   25 +++-
 drivers/scsi/ufs/ufshcd-pci.c|   60 ++-
 drivers/scsi/ufs/ufshcd-pltfrm.c |   41 +
 drivers/scsi/ufs/ufshcd.c|  337 ++
 drivers/scsi/ufs/ufshcd.h|   10 +
 5 files changed, 466 insertions(+), 7 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 V2 2/2] scsi: ufs: Add runtime PM helpers for UFS host driver

2013-06-28 Thread Sujit Reddy Thumma
Add runtime PM helpers to suspend/resume the UFS controller
device at runtime.

Signed-off-by: Sujit Reddy Thumma 
---
 drivers/scsi/ufs/ufshcd-pci.c|   60 ++
 drivers/scsi/ufs/ufshcd-pltfrm.c |   41 ++
 2 files changed, 95 insertions(+), 6 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
index 64d36eb..f669e9a2 100644
--- a/drivers/scsi/ufs/ufshcd-pci.c
+++ b/drivers/scsi/ufs/ufshcd-pci.c
@@ -35,6 +35,7 @@
 
 #include "ufshcd.h"
 #include 
+#include 
 
 #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
@@ -156,6 +197,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 */
@@ -169,10 +218,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..7541054 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -34,6 +34,7 @@
  */
 
 #include 
+#include 
 
 #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 ufs_hba *hba =  platform_get_drvdata(pdev);
+
+   if (!hba)
+   return 0;
+
+   return ufshcd_runtime_resume(hba);
+}
+static int ufshcd_pltfrm_runtime_idle(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_idle(hba);
+}
+#else /* !CONFIG_PM_RUNTIME */
+#define ufshcd_pltfrm_runtime_suspend  NULL
+#define ufshcd_pltfrm_runtime_resume   NULL
+#define ufshcd_pltfrm_runtime_idle NULL
+#endif /* CONFIG_PM_RUNTIME */
+
 /**
  * ufshcd_pltfrm_probe - probe routine of the driver
  * @pdev: pointer to Platform device handle
@@ -157,6 +195,9 @@ static const struct of_device_id uf

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

2013-06-28 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 
---
 drivers/scsi/ufs/ufs.h|   25 -
 drivers/scsi/ufs/ufshcd.c |  337 +
 drivers/scsi/ufs/ufshcd.h |   10 ++
 3 files changed, 371 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 5484c59..24e589c 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 f8278be..ac323a8 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -300,6 +300,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
@@ -1206,6 +1221,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_re

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

2013-06-28 Thread Sujit Reddy Thumma
From: Dolev Raviv 

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 
Signed-off-by: Sujit Reddy Thumma 
---
 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 4737fad..5484c59 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-6
+ * @reserved: spec reserved DW-7,8
+ */
+struct utp_upiu_query {
+   u8 opcode;
+   u8 idn;
+   u8 index;
+   u8 selector;
+   u16 reserved_osf;
+   u16 length;
+   u32 value;
+   u32 reserved[2];
+};
+
+/**
  * struct utp_upiu_req - general upiu request structure
  * @header:UPIU header structure DW-0 to DW-2
  * @sc: fields structure for scsi command
+ * @qr: fields structure for query request
  */
 struct utp_upiu_req {
struct utp_upiu_header header;
-   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
+ * @qr: fields structure for query request
  */
 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 +283,24 @@ struct utp_upiu_task_rsp {
u32 reserved[3];
 };
 
+/**
+ * struct ufs_query_req - pa

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

2013-06-28 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 
Signed-off-by: Dolev Raviv 
---
 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..4737fad 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -36,10 +36,16 @@
 #ifndef _UFS_H
 #define _UFS_H
 
+#include 
+#include 
+
 #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
+ */
+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
+ */
+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 431ddb2..d613ae4 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 register value after iteration
+ */
+static u32 ufshcd_wa

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

2013-06-28 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.


Depends on:
[PATCH 01/10] scsi: ufs: wrap the i/o access operations
[PATCH 02/10] scsi: ufs: amend interrupt configuration
[PATCH 03/10] scsi: ufs: remove version check before IS reg clear
[PATCH 04/10] scsi: ufs: rework link start-up process
[PATCH 05/10] scsi: ufs: Fix the response UPIU length setting
[PATCH 06/10] scsi: ufs: use devres functions for ufshcd
[PATCH 07/10] ufshcd-pltfrm: add missing empty slot in ufs_of_match[]
[PATCH 08/10] ufs: fix register address in UIC error interrupt handling
[PATCH 09/10] ufshcd-pltfrm: remove unnecessary dma_set_coherent_mask() call
[PATCH 10/10] ufs: fix DMA mask setting

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


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

2013-06-28 Thread Sujit Reddy Thumma

On 6/26/2013 11:06 PM, Santosh Y wrote:

+/**
>   * ufshcd_memory_alloc - allocate memory for host memory space data
>structures
>   * @hba: per adapter instance
>   *
>@@ -1803,6 +1904,9 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
>struct ufshcd_lrb *lrbp)
>  */
> scsi_status = result & MASK_SCSI_STATUS;
> result = ufshcd_scsi_cmd_status(lrbp,
>scsi_status);
>+
>+   if (ufshcd_is_exception_event(lrbp->ucd_rsp_ptr))

 ^
This condition will not satisfy until a runtime suspend/resume cycle
completes. Is there any specific reason it is only being enabled in
runtime-resume routine?
As the fBackgroundOpsEn = 1 by default, shouldn't the sequence be 1.
ufshcd_disable_auto_bkops() 2. ufshcd_enable_ee() during the startup.



According to the specification, the host should put in all measures to 
ensure that the device doesn't enter into URGENT_BKOPS need because the 
device may not operate optimally in that case. If your sequence is used 
then we expect a case where URGENT_BKOPS is needed if the runtime PM is 
disabled or prohibited soon after startup.


We wouldn't want to disable auto bkops by default without knowing that 
the runtime PM is enabled, since the device/host/software idleness is 
determined by runtime PM operation as explained in the commit text.


--
Regards,
Sujit
--
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


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

2013-06-28 Thread Sujit Reddy Thumma

On 6/27/2013 4:49 PM, Santosh Y wrote:

>+   spin_lock_irqsave(host->host_lock, flags);
> task_req_descp = hba->utmrdl_base_addr;
> task_req_descp += free_slot;
>
>@@ -2353,38 +2387,39 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba,
> (struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
> task_req_upiup->header.dword_0 =
> UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
>- lrbp->lun, lrbp->task_tag);
>+   lun_id, free_slot);

Actually it still doesn't fix the problem. The*task tag*  used here
should be unique across the SCSI/Query and Task Managment UPIUs.


I am sorry, I didn't get that. Why should it be unique across the 
SCSI/Query? For example, if a machine supports 32 request slots and 8 
task management slots, then the task management command tag can be 
anything out of 8 slots.


--
Regards,
Sujit
--
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


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

2013-06-14 Thread Sujit Reddy Thumma

On 6/13/2013 10:03 AM, Sujit Reddy Thumma wrote:

  static struct scsi_host_template ufshcd_driver_template = {
@@ -1771,8 +2064,8 @@ int ufshcd_init(struct device *dev, struct
ufs_hba **hba_handle,
 /* Configure LRB */
 ufshcd_host_memory_configure(hba);

-   host->can_queue = hba->nutrs;
-   host->cmd_per_lun = hba->nutrs;
+   host->can_queue = SCSI_CMD_QUEUE_SIZE;



I don't think this is appropriate. Reserving a slot exclusively for
query/DM requests is not optimal.  can_queue should be changed
dynamically, scsi_adjust_queue_depth() maybe?


The motivation to change the design for this patch  is that routing
query command through SCSI layer raised problems when we are trying to
improve the fatal error handling as we need to block the SCSI layer and
recover the link. Hence, the need to have the query/DM command send as
internal commands. Which probably makes sense.

One possible option was to look for a free command slot whenever we
try to send an internal command, but getting a free slot is not always 
guaranteed.


Even if we get hold of a tag, there is no way we can explain this to
SCSI/block layer that particular tag is in use internally (case where
normal query requests are sent in tandem with SCSI requests). In which
case, other option is to use tag[31] as you have said on need basis
and change the queue depth to 31. This again has problems - one
changing queue depth doesn't take effect immediately but for the next
command. Second, if the command in tag[31] is the root cause of the
fatal error and is stuck, then the internal command has to wait forever
(until scsi timesout) to plan recovery. Considering, all these factors
it is better to reserve a tag and use it for internal commands instead 
of playing around with the tags internally without/partially informing 
SCSI/block layers.


I am open for discussion, if there are any suggestions to handle such 
LLD requirements in the SCSI layer optimally.


Coming to how optimal is to work with 31 slots instead of h/w defined 32 
is something which we can answer when we have true multi queueing. 
AFAIK, there may not exist real-world applications which utilize full 32 
tags at particular instant. SATA AHCI controller driver which is 
ubiquitous in modern systems also reserves a slot for internal commands 
which is used only in case of error handling and AFAIK, no one has ever 
reported performance problems with this (its about 7 years the commit to 
reserve a slot is merged into Linux tree).


I hope this explains the intent. Please let me know what do you think.

--
Regards,
Sujit
--
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 1/4] scsi: ufs: Fix broken task management command implementation

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

- 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.

- 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.

- 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.

- While preparing the TM command descriptor the tag used should be the
free slot of TM command and not the task tag of the command which
the TM command is trying to manage.
Fix: Use free slot tag instead of task tag of SCSI command

- 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 
---
 drivers/scsi/ufs/ufshcd.c |  174 +
 drivers/scsi/ufs/ufshcd.h |6 +-
 2 files changed, 117 insertions(+), 63 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index aa1f11e..7d043f4 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 */
+
 #define SCSI_CMD_QUEUE_SIZE (hba->nutrs - 1)
 /* Reserved tag for internal commands */
 #define INTERNAL_CMD_TAG   (hba->nutrs - 1)
@@ -188,13 +191,32 @@ 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;
+
+   do {
+   tag = find_first_zero_bit(
+   &hba->outstanding_tasks, hba->nutmrs);
+   if (tag >= hba->nutmrs)
+   return false;
+   } while (test_and_set_bit_lock(tag, &hba->outstanding_tasks));
+
+   if (free_slot)
+   *free_slot = tag;
+
+   return true;
+}
+
+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->outstanding_tasks);
 }
 
 /**
@@ -1745,10 +1767,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;
@@ -1758,9 +1781,6 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 
index)
 
spin_lock_irqsave(hba->host->host_lock, flags);
 
-   /* Clear completed tasks from outstanding_tasks */
-   __clear_bit(index, &hba->outstanding_tasks);
-
task_req_descp = hba->utmrdl_base_addr;
ocs_value = ufshcd_get_tmr_ocs(&task_req_descp[index]);
 
@@ -1769,19 +1789,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",
+   

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

2013-06-13 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 
---
 drivers/scsi/ufs/ufshcd.c |  348 +++--
 drivers/scsi/ufs/ufshcd.h |2 +
 drivers/scsi/ufs/ufshci.h |   19 ++-
 3 files changed, 293 insertions(+), 76 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index e368bb0..cca774e 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -84,6 +84,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,
@@ -112,6 +120,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
@@ -1570,9 +1579,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;
 }
@@ -1698,65 +1704,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 < SCSI_CMD_QUEUE_SIZE; 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;
-   }
-   }
-   }
-
-   /* complete internal command */
-   if (hba->i_cmd.dev_cmd_complete)
-   complete(hba->i_cmd.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
  *
@@ -1773,6 +1720,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;
+
/*

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

2013-06-13 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. It can happen
that when the abort command is issued, the device doesn't have the
command pending but just before the command is cleared in controller
the command is comitted to the device by h/w. In this case, the
command is still pending in the device but the host controller and
s/w assume it is aborted which can confuse the device h/w and further
operations might fail.

To avoid this, query task presence in the device before sending abort
task, task managment 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 
---
 drivers/scsi/ufs/ufshcd.c |   71 ++--
 1 files changed, 55 insertions(+), 16 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 7d043f4..d576df2 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2502,6 +2502,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)
@@ -2510,7 +2516,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 = 100;
u8 resp;
struct ufshcd_lrb *lrbp;
 
@@ -2518,37 +2525,69 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
hba = shost_priv(host);
tag = cmd->request->tag;
 
-   spin_lock_irqsave(host->host_lock, flags);
-
-   /* check if command is still pending */
-   if (!(test_bit(tag, &hba->outstanding_reqs))) {
-   err = FAILED;
-   spin_unlock_irqrestore(host->host_lock, flags);
+   /* If command is already aborted/completed, return SUCCESS */
+   if (!(test_bit(tag, &hba->outstanding_reqs)))
goto out;
-   }
-   spin_unlock_irqrestore(host->host_lock, flags);
 
lrbp = &hba->lrb[tag];
+   for (;;) {
+   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);
+   if (poll_cnt) {
+   poll_cnt--;
+   continue;
+   }
+   err = -EBUSY;
+   }
+   /* command completed already */
+   goto out;
+   } else {
+   if (!err)
+   err = -EPERM; /* service response error */
+   goto out;
+   }
+   }
+
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 = -EPERM; /* 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);
 out:
+   if (!err) {
+   err = SUCCESS;
+   } else {
+   dev_err(hba->dev, "%s: failed with err %d\n&quo

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

2013-06-13 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 
---
 drivers/scsi/ufs/ufshcd.c |  446 +++--
 drivers/scsi/ufs/ufshcd.h |2 +
 2 files changed, 391 insertions(+), 57 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index d576df2..e368bb0 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -73,9 +73,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 */
@@ -91,6 +97,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
@@ -879,9 +901,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);
+   break;
}
 
lrbp = &hba->lrb[tag];
@@ -1538,8 +1573,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;
 }
@@ -2229,6 +2262,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:

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

2013-06-13 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 2/8] scsi: ufs: wrap the i/o access operations
[PATCH 3/8] scsi: ufs: amend interrupt configuration
[PATCH 4/8] scsi: ufs: remove version check before IS reg clear
[PATCH 5/8] scsi: ufs: rework link start-up process
[PATCH 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
[PATCH 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization
[PATCH 1/2] scsi: ufs: Add support for host assisted background operations
[PATCH 2/2] scsi: ufs: Add runtime PM helpers for UFS host driver


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 | 1009 -
 drivers/scsi/ufs/ufshcd.h |   10 +-
 drivers/scsi/ufs/ufshci.h |   19 +-
 3 files changed, 841 insertions(+), 197 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 0/2] scsi: ufs: Add support to control UFS device background operations

2013-06-13 Thread Sujit Reddy Thumma
Add host assisted background operations for UFS device and runtime PM
helpers for ufshcd platform and pci glue drivers. The background operations
are disabled during runtime resume and enabled when the device is idle and
runtime suspended.

These patches depends on:
[PATCH 2/8] scsi: ufs: wrap the i/o access operations
[PATCH 3/8] scsi: ufs: amend interrupt configuration
[PATCH 4/8] scsi: ufs: remove version check before IS reg clear
[PATCH 5/8] scsi: ufs: rework link start-up process
[PATCH 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
[PATCH 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization

Sujit Reddy Thumma (2):
  scsi: ufs: Add support for host assisted background operations
  scsi: ufs: Add runtime PM helpers for UFS host driver

 drivers/scsi/ufs/ufs.h   |   25 +++-
 drivers/scsi/ufs/ufshcd-pci.c|   60 ++-
 drivers/scsi/ufs/ufshcd-pltfrm.c |   41 +
 drivers/scsi/ufs/ufshcd.c|  343 ++
 drivers/scsi/ufs/ufshcd.h|   10 +
 5 files changed, 472 insertions(+), 7 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


  1   2   >