[PATCH v8 14/17] scsi: ufs: add device quirk delay before putting UFS rails in LPM

2016-03-10 Thread Yaniv Gardi
We put the UFS device in sleep state & UFS link in hibern8 state during
runtime suspend. After this we put all the UFS rails in low power
modes immediately but it seems some devices may still draw more than
sleep current from UFS rails (especially from VCCQ rail) at-least for
500us.
To avoid this situation, this change adds 2ms delay before putting
these UFS rails in LPM mode.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs_quirks.h | 14 +-
 drivers/scsi/ufs/ufshcd.c | 10 ++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h
index 62ebd89..ee4ab85 100644
--- a/drivers/scsi/ufs/ufs_quirks.h
+++ b/drivers/scsi/ufs/ufs_quirks.h
@@ -18,7 +18,7 @@
 /* return true if s1 is a prefix of s2 */
 #define STR_PRFX_EQUAL(s1, s2) !strncmp(s1, s2, strlen(s1))
 
-#define UFS_ANY_VENDOR -1
+#define UFS_ANY_VENDOR 0x
 #define UFS_ANY_MODEL  "ANY_MODEL"
 
 #define MAX_MODEL_LEN 16
@@ -119,16 +119,28 @@ struct ufs_dev_fix {
  */
 #define UFS_DEVICE_NO_FASTAUTO (1 << 5)
 
+/*
+ * It seems some UFS devices may keep drawing more than sleep current
+ * (atleast for 500us) from UFS rails (especially from VCCQ rail).
+ * To avoid this situation, add 2ms delay before putting these UFS
+ * rails in LPM mode.
+ */
+#define UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM  (1 << 6)
+
 struct ufs_hba;
 void ufs_advertise_fixup_device(struct ufs_hba *hba);
 
 static struct ufs_dev_fix ufs_fixups[] = {
/* UFS cards deviations table */
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_NO_FASTAUTO),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
UFS_DEVICE_QUIRK_PA_TACTIVATE),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 93cc02f..f8fa72c 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5716,6 +5716,16 @@ out:
 static void ufshcd_vreg_set_lpm(struct ufs_hba *hba)
 {
/*
+* It seems some UFS devices may keep drawing more than sleep current
+* (atleast for 500us) from UFS rails (especially from VCCQ rail).
+* To avoid this situation, add 2ms delay before putting these UFS
+* rails in LPM mode.
+*/
+   if (!ufshcd_is_link_active(hba) &&
+   hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM)
+   usleep_range(2000, 2100);
+
+   /*
 * If UFS device is either in UFS_Sleep turn off VCC rail to save some
 * power.
 *
-- 
1.8.5.2

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


[PATCH v8 08/17] scsi: ufs: make error handling bit faster

2016-03-10 Thread Yaniv Gardi
UFS driver's error handler forcefully tries to clear all the pending
requests. For each pending request in the queue, it waits 1 sec for it
to get cleared. If we have multiple requests in the queue then it's
possible that we might end up waiting for those many seconds before
resetting the host. But note that resetting host would any way clear
all the pending requests from the hardware. Hence this change skips
the forceful clear of the pending requests if we are anyway going to
reset the host (for fatal errors).

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 155 +-
 1 file changed, 112 insertions(+), 43 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 8ed7d4b..adaae34 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -133,9 +133,11 @@ enum {
 /* 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 */
+   UFSHCD_UIC_DL_NAC_RECEIVED_ERROR = (1 << 1), /* Data link layer error */
+   UFSHCD_UIC_DL_TCx_REPLAY_ERROR = (1 << 2), /* Data link layer error */
+   UFSHCD_UIC_NL_ERROR = (1 << 3), /* Network layer error */
+   UFSHCD_UIC_TL_ERROR = (1 << 4), /* Transport Layer error */
+   UFSHCD_UIC_DME_ERROR = (1 << 5), /* DME error */
 };
 
 /* Interrupt configuration options */
@@ -3465,31 +3467,18 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, 
u32 intr_status)
 }
 
 /**
- * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * __ufshcd_transfer_req_compl - handle SCSI and query command completion
  * @hba: per adapter instance
+ * @completed_reqs: requests to complete
  */
-static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
+   unsigned long completed_reqs)
 {
struct ufshcd_lrb *lrbp;
struct scsi_cmnd *cmd;
-   unsigned long completed_reqs;
-   u32 tr_doorbell;
int result;
int index;
 
-   /* Resetting interrupt aggregation counters first and reading the
-* DOOR_BELL afterward allows us to handle all the completed requests.
-* In order to prevent other interrupts starvation the DB is read once
-* after reset. The down side of this solution is the possibility of
-* false interrupt if device completes another request after resetting
-* aggregation and before reading the DB.
-*/
-   if (ufshcd_is_intr_aggr_allowed(hba))
-   ufshcd_reset_intr_aggr(hba);
-
-   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
-   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
-
for_each_set_bit(index, _reqs, hba->nutrs) {
lrbp = >lrb[index];
cmd = lrbp->cmd;
@@ -3519,6 +3508,31 @@ static void ufshcd_transfer_req_compl(struct ufs_hba 
*hba)
 }
 
 /**
+ * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+{
+   unsigned long completed_reqs;
+   u32 tr_doorbell;
+
+   /* Resetting interrupt aggregation counters first and reading the
+* DOOR_BELL afterward allows us to handle all the completed requests.
+* In order to prevent other interrupts starvation the DB is read once
+* after reset. The down side of this solution is the possibility of
+* false interrupt if device completes another request after resetting
+* aggregation and before reading the DB.
+*/
+   if (ufshcd_is_intr_aggr_allowed(hba))
+   ufshcd_reset_intr_aggr(hba);
+
+   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
+
+   __ufshcd_transfer_req_compl(hba, completed_reqs);
+}
+
+/**
  * ufshcd_disable_ee - disable exception event
  * @hba: per-adapter instance
  * @mask: exception event to disable
@@ -3773,6 +3787,13 @@ out:
return;
 }
 
+/* Complete requests that have door-bell cleared */
+static void ufshcd_complete_requests(struct ufs_hba *hba)
+{
+   ufshcd_transfer_req_compl(hba);
+   ufshcd_tmc_handler(hba);
+}
+
 /**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
@@ -3785,6 +3806,7 @@ static void ufshcd_err_handler(struct work_struct *work)
u32 err_tm = 0;
int err = 0;
int tag;
+

[PATCH v8 14/17] scsi: ufs: add device quirk delay before putting UFS rails in LPM

2016-03-10 Thread Yaniv Gardi
We put the UFS device in sleep state & UFS link in hibern8 state during
runtime suspend. After this we put all the UFS rails in low power
modes immediately but it seems some devices may still draw more than
sleep current from UFS rails (especially from VCCQ rail) at-least for
500us.
To avoid this situation, this change adds 2ms delay before putting
these UFS rails in LPM mode.

Reviewed-by: Gilad Broner 
Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs_quirks.h | 14 +-
 drivers/scsi/ufs/ufshcd.c | 10 ++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h
index 62ebd89..ee4ab85 100644
--- a/drivers/scsi/ufs/ufs_quirks.h
+++ b/drivers/scsi/ufs/ufs_quirks.h
@@ -18,7 +18,7 @@
 /* return true if s1 is a prefix of s2 */
 #define STR_PRFX_EQUAL(s1, s2) !strncmp(s1, s2, strlen(s1))
 
-#define UFS_ANY_VENDOR -1
+#define UFS_ANY_VENDOR 0x
 #define UFS_ANY_MODEL  "ANY_MODEL"
 
 #define MAX_MODEL_LEN 16
@@ -119,16 +119,28 @@ struct ufs_dev_fix {
  */
 #define UFS_DEVICE_NO_FASTAUTO (1 << 5)
 
+/*
+ * It seems some UFS devices may keep drawing more than sleep current
+ * (atleast for 500us) from UFS rails (especially from VCCQ rail).
+ * To avoid this situation, add 2ms delay before putting these UFS
+ * rails in LPM mode.
+ */
+#define UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM  (1 << 6)
+
 struct ufs_hba;
 void ufs_advertise_fixup_device(struct ufs_hba *hba);
 
 static struct ufs_dev_fix ufs_fixups[] = {
/* UFS cards deviations table */
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_NO_FASTAUTO),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
UFS_DEVICE_QUIRK_PA_TACTIVATE),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 93cc02f..f8fa72c 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5716,6 +5716,16 @@ out:
 static void ufshcd_vreg_set_lpm(struct ufs_hba *hba)
 {
/*
+* It seems some UFS devices may keep drawing more than sleep current
+* (atleast for 500us) from UFS rails (especially from VCCQ rail).
+* To avoid this situation, add 2ms delay before putting these UFS
+* rails in LPM mode.
+*/
+   if (!ufshcd_is_link_active(hba) &&
+   hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM)
+   usleep_range(2000, 2100);
+
+   /*
 * If UFS device is either in UFS_Sleep turn off VCC rail to save some
 * power.
 *
-- 
1.8.5.2

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


[PATCH v8 08/17] scsi: ufs: make error handling bit faster

2016-03-10 Thread Yaniv Gardi
UFS driver's error handler forcefully tries to clear all the pending
requests. For each pending request in the queue, it waits 1 sec for it
to get cleared. If we have multiple requests in the queue then it's
possible that we might end up waiting for those many seconds before
resetting the host. But note that resetting host would any way clear
all the pending requests from the hardware. Hence this change skips
the forceful clear of the pending requests if we are anyway going to
reset the host (for fatal errors).

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 155 +-
 1 file changed, 112 insertions(+), 43 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 8ed7d4b..adaae34 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -133,9 +133,11 @@ enum {
 /* 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 */
+   UFSHCD_UIC_DL_NAC_RECEIVED_ERROR = (1 << 1), /* Data link layer error */
+   UFSHCD_UIC_DL_TCx_REPLAY_ERROR = (1 << 2), /* Data link layer error */
+   UFSHCD_UIC_NL_ERROR = (1 << 3), /* Network layer error */
+   UFSHCD_UIC_TL_ERROR = (1 << 4), /* Transport Layer error */
+   UFSHCD_UIC_DME_ERROR = (1 << 5), /* DME error */
 };
 
 /* Interrupt configuration options */
@@ -3465,31 +3467,18 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, 
u32 intr_status)
 }
 
 /**
- * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * __ufshcd_transfer_req_compl - handle SCSI and query command completion
  * @hba: per adapter instance
+ * @completed_reqs: requests to complete
  */
-static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
+   unsigned long completed_reqs)
 {
struct ufshcd_lrb *lrbp;
struct scsi_cmnd *cmd;
-   unsigned long completed_reqs;
-   u32 tr_doorbell;
int result;
int index;
 
-   /* Resetting interrupt aggregation counters first and reading the
-* DOOR_BELL afterward allows us to handle all the completed requests.
-* In order to prevent other interrupts starvation the DB is read once
-* after reset. The down side of this solution is the possibility of
-* false interrupt if device completes another request after resetting
-* aggregation and before reading the DB.
-*/
-   if (ufshcd_is_intr_aggr_allowed(hba))
-   ufshcd_reset_intr_aggr(hba);
-
-   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
-   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
-
for_each_set_bit(index, _reqs, hba->nutrs) {
lrbp = >lrb[index];
cmd = lrbp->cmd;
@@ -3519,6 +3508,31 @@ static void ufshcd_transfer_req_compl(struct ufs_hba 
*hba)
 }
 
 /**
+ * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+{
+   unsigned long completed_reqs;
+   u32 tr_doorbell;
+
+   /* Resetting interrupt aggregation counters first and reading the
+* DOOR_BELL afterward allows us to handle all the completed requests.
+* In order to prevent other interrupts starvation the DB is read once
+* after reset. The down side of this solution is the possibility of
+* false interrupt if device completes another request after resetting
+* aggregation and before reading the DB.
+*/
+   if (ufshcd_is_intr_aggr_allowed(hba))
+   ufshcd_reset_intr_aggr(hba);
+
+   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
+
+   __ufshcd_transfer_req_compl(hba, completed_reqs);
+}
+
+/**
  * ufshcd_disable_ee - disable exception event
  * @hba: per-adapter instance
  * @mask: exception event to disable
@@ -3773,6 +3787,13 @@ out:
return;
 }
 
+/* Complete requests that have door-bell cleared */
+static void ufshcd_complete_requests(struct ufs_hba *hba)
+{
+   ufshcd_transfer_req_compl(hba);
+   ufshcd_tmc_handler(hba);
+}
+
 /**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
@@ -3785,6 +3806,7 @@ static void ufshcd_err_handler(struct work_struct *work)
u32 err_tm = 0;
int err = 0;
int tag;
+   bool needs_reset = false;
 
hba = container_of(work, struct ufs_hba, eh_w

[PATCH v8 10/17] scsi: ufs: add retry for query descriptors

2016-03-10 Thread Yaniv Gardi
Query commands have 100ms timeout and it may timeout if they are
issued in parallel to ongoing read/write SCSI commands, this change
adds the retry (max: 10) in case command timeouts.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 55 +++
 1 file changed, 37 insertions(+), 18 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 4eedb7f..b429a57 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1875,21 +1875,7 @@ static int ufshcd_query_attr_retry(struct ufs_hba *hba,
return ret;
 }
 
-/**
- * ufshcd_query_descriptor - API function for sending descriptor requests
- * hba: per-adapter instance
- * opcode: attribute opcode
- * idn: attribute idn to access
- * index: index field
- * selector: selector field
- * desc_buf: the buffer that contains the descriptor
- * buf_len: length parameter passed to the device
- *
- * Returns 0 for success, non-zero in case of failure.
- * The buf_len parameter will contain, on return, the length parameter
- * received on the response.
- */
-static int ufshcd_query_descriptor(struct ufs_hba *hba,
+static int __ufshcd_query_descriptor(struct ufs_hba *hba,
enum query_opcode opcode, enum desc_idn idn, u8 index,
u8 selector, u8 *desc_buf, int *buf_len)
 {
@@ -1954,6 +1940,39 @@ out:
 }
 
 /**
+ * ufshcd_query_descriptor_retry - API function for sending descriptor
+ * requests
+ * hba: per-adapter instance
+ * opcode: attribute opcode
+ * idn: attribute idn to access
+ * index: index field
+ * selector: selector field
+ * desc_buf: the buffer that contains the descriptor
+ * buf_len: length parameter passed to the device
+ *
+ * Returns 0 for success, non-zero in case of failure.
+ * The buf_len parameter will contain, on return, the length parameter
+ * received on the response.
+ */
+int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
+   enum query_opcode opcode, enum desc_idn idn, u8 index,
+   u8 selector, u8 *desc_buf, int *buf_len)
+{
+   int err;
+   int retries;
+
+   for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
+   err = __ufshcd_query_descriptor(hba, opcode, idn, index,
+   selector, desc_buf, buf_len);
+   if (!err || err == -EINVAL)
+   break;
+   }
+
+   return err;
+}
+EXPORT_SYMBOL(ufshcd_query_descriptor_retry);
+
+/**
  * ufshcd_read_desc_param - read the specified descriptor parameter
  * @hba: Pointer to adapter instance
  * @desc_id: descriptor idn value
@@ -1995,9 +2014,9 @@ static int ufshcd_read_desc_param(struct ufs_hba *hba,
return -ENOMEM;
}
 
-   ret = ufshcd_query_descriptor(hba, UPIU_QUERY_OPCODE_READ_DESC,
- desc_id, desc_index, 0, desc_buf,
- _len);
+   ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC,
+   desc_id, desc_index, 0, desc_buf,
+   _len);
 
if (ret || (buff_len < ufs_query_desc_max_size[desc_id]) ||
(desc_buf[QUERY_DESC_LENGTH_OFFSET] !=
-- 
1.8.5.2

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


[PATCH v8 07/17] scsi: ufs: disable vccq if it's not needed by UFS device

2016-03-10 Thread Yaniv Gardi
Some UFS devices don't require VCCQ rail for device operations hence
this change adds support to recognize such devices and remove vote for
the unused VCCQ rail.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs.h|  1 +
 drivers/scsi/ufs/ufshcd.c | 60 +++
 2 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 8ec6356..b291fa6 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -501,6 +501,7 @@ struct ufs_vreg {
struct regulator *reg;
const char *name;
bool enabled;
+   bool unused;
int min_uV;
int max_uV;
int min_uA;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index f8b458f..8ed7d4b 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -195,6 +195,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba);
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
 bool skip_ref_clk);
 static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused);
 static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba);
 static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
 static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
@@ -4662,6 +4663,12 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
goto out;
 
ufs_advertise_fixup_device(hba);
+
+   ret = ufshcd_set_vccq_rail_unused(hba,
+   (hba->dev_quirks & UFS_DEVICE_NO_VCCQ) ? true : false);
+   if (ret)
+   goto out;
+
/* UFS device is also active now */
ufshcd_set_ufs_dev_active(hba);
ufshcd_force_reset_auto_bkops(hba);
@@ -4812,13 +4819,24 @@ static int ufshcd_config_vreg_load(struct device *dev, 
struct ufs_vreg *vreg,
 static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg,
+  UFS_VREG_LPM_LOAD_UA);
 }
 
 static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
 }
 
 static int ufshcd_config_vreg(struct device *dev,
@@ -4853,7 +4871,9 @@ static int ufshcd_enable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (vreg->enabled || vreg->unused)
goto out;
 
ret = ufshcd_config_vreg(dev, vreg, true);
@@ -4873,7 +4893,9 @@ static int ufshcd_disable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || !vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (!vreg->enabled || vreg->unused)
goto out;
 
ret = regulator_disable(vreg->reg);
@@ -4979,6 +5001,36 @@ static int ufshcd_init_hba_vreg(struct ufs_hba *hba)
return 0;
 }
 
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused)
+{
+   int ret = 0;
+   struct ufs_vreg_info *info = >vreg_info;
+
+   if (!info)
+   goto out;
+   else if (!info->vccq)
+   goto out;
+
+   if (unused) {
+   /* shut off the rail here */
+   ret = ufshcd_toggle_vreg(hba->dev, info->vccq, false);
+   /*
+* Mark this rail as no longer used, so it doesn't get enabled
+* later by mistake
+*/
+   if (!ret)
+   info->vccq->unused = true;
+   } else {
+   /*
+* rail should have been already enabled hence just make sure
+* that unused flag is cleared.
+*/
+   info->vccq->unused = false;
+   }
+out:
+   return ret;
+}
+
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
bool skip_ref_clk)
 {
-- 
1.8.5.2

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


[PATCH v8 16/17] scsi: ufs-qcom: enable/disable the device ref clock

2016-03-10 Thread Yaniv Gardi
This change enables the device ref clock before changing to HS mode
and disables it if entered to PWM mode.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs-qcom.c | 12 
 drivers/scsi/ufs/ufshcd.h   |  9 +
 2 files changed, 21 insertions(+)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 966bacf..849fcf3 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -980,6 +980,10 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
goto out;
}
 
+   /* enable the device ref clock before changing to HS mode */
+   if (!ufshcd_is_hs_mode(>pwr_info) &&
+   ufshcd_is_hs_mode(dev_req_params))
+   ufs_qcom_dev_ref_clk_ctrl(host, true);
break;
case POST_CHANGE:
if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx,
@@ -1007,6 +1011,11 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba 
*hba,
memcpy(>dev_req_params,
dev_req_params, sizeof(*dev_req_params));
ufs_qcom_update_bus_bw_vote(host);
+
+   /* disable the device ref clock if entered PWM mode */
+   if (ufshcd_is_hs_mode(>pwr_info) &&
+   !ufshcd_is_hs_mode(dev_req_params))
+   ufs_qcom_dev_ref_clk_ctrl(host, false);
break;
default:
ret = -EINVAL;
@@ -1108,6 +1117,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, 
bool on)
ufs_qcom_phy_disable_iface_clk(host->generic_phy);
goto out;
}
+   /* enable the device ref clock for HS mode*/
+   if (ufshcd_is_hs_mode(>pwr_info))
+   ufs_qcom_dev_ref_clk_ctrl(host, true);
vote = host->bus_vote.saved_vote;
if (vote == host->bus_vote.min_bw_vote)
ufs_qcom_update_bus_bw_vote(host);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 41d9bfd..4bb6566 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -54,6 +54,7 @@
 #include 
 #include 
 #include 
+#include "unipro.h"
 
 #include 
 #include 
@@ -689,6 +690,14 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
 
 int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size);
 
+static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info)
+{
+   return (pwr_info->pwr_rx == FAST_MODE ||
+   pwr_info->pwr_rx == FASTAUTO_MODE) &&
+   (pwr_info->pwr_tx == FAST_MODE ||
+   pwr_info->pwr_tx == FASTAUTO_MODE);
+}
+
 #define ASCII_STD true
 
 int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
-- 
1.8.5.2

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


[PATCH v8 05/17] scsi: ufs: add support to read device and string descriptors

2016-03-10 Thread Yaniv Gardi
This change adds support to read device descriptor and string descriptor
from a UFS device

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Reviewed-by: Hannes Reinecke <h...@suse.com>
Signed-off-by: Raviv Shvili <rshv...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs.h|  1 +
 drivers/scsi/ufs/ufshcd.c | 88 ++-
 drivers/scsi/ufs/ufshcd.h |  7 
 3 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 54a16ce..aacb235 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -43,6 +43,7 @@
 #define GENERAL_UPIU_REQUEST_SIZE 32
 #define QUERY_DESC_MAX_SIZE   255
 #define QUERY_DESC_MIN_SIZE   2
+#define QUERY_DESC_HDR_SIZE   2
 #define QUERY_OSF_SIZE(GENERAL_UPIU_REQUEST_SIZE - \
(sizeof(struct utp_upiu_header)))
 
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 80031e6..e2ed415 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -39,7 +39,7 @@
 
 #include 
 #include 
-
+#include 
 #include 
 #include "ufshcd.h"
 #include "unipro.h"
@@ -232,6 +232,16 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
}
 }
 
+/* replace non-printable or non-ASCII characters with spaces */
+static inline void ufshcd_remove_non_printable(char *val)
+{
+   if (!val)
+   return;
+
+   if (*val < 0x20 || *val > 0x7e)
+   *val = ' ';
+}
+
 /*
  * ufshcd_wait_for_register - wait for register value to change
  * @hba - per-adapter interface
@@ -2021,6 +2031,82 @@ static inline int ufshcd_read_power_desc(struct ufs_hba 
*hba,
return ufshcd_read_desc(hba, QUERY_DESC_IDN_POWER, 0, buf, size);
 }
 
+int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size)
+{
+   return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, buf, size);
+}
+EXPORT_SYMBOL(ufshcd_read_device_desc);
+
+/**
+ * ufshcd_read_string_desc - read string descriptor
+ * @hba: pointer to adapter instance
+ * @desc_index: descriptor index
+ * @buf: pointer to buffer where descriptor would be read
+ * @size: size of buf
+ * @ascii: if true convert from unicode to ascii characters
+ *
+ * Return 0 in case of success, non-zero otherwise
+ */
+int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
+   u32 size, bool ascii)
+{
+   int err = 0;
+
+   err = ufshcd_read_desc(hba,
+   QUERY_DESC_IDN_STRING, desc_index, buf, size);
+
+   if (err) {
+   dev_err(hba->dev, "%s: reading String Desc failed after %d 
retries. err = %d\n",
+   __func__, QUERY_REQ_RETRIES, err);
+   goto out;
+   }
+
+   if (ascii) {
+   int desc_len;
+   int ascii_len;
+   int i;
+   char *buff_ascii;
+
+   desc_len = buf[0];
+   /* remove header and divide by 2 to move from UTF16 to UTF8 */
+   ascii_len = (desc_len - QUERY_DESC_HDR_SIZE) / 2 + 1;
+   if (size < ascii_len + QUERY_DESC_HDR_SIZE) {
+   dev_err(hba->dev, "%s: buffer allocated size is too 
small\n",
+   __func__);
+   err = -ENOMEM;
+   goto out;
+   }
+
+   buff_ascii = kmalloc(ascii_len, GFP_KERNEL);
+   if (!buff_ascii) {
+   err = -ENOMEM;
+   goto out_free_buff;
+   }
+
+   /*
+* the descriptor contains string in UTF16 format
+* we need to convert to utf-8 so it can be displayed
+*/
+   utf16s_to_utf8s((wchar_t *)[QUERY_DESC_HDR_SIZE],
+   desc_len - QUERY_DESC_HDR_SIZE,
+   UTF16_BIG_ENDIAN, buff_ascii, ascii_len);
+
+   /* replace non-printable or non-ASCII characters with spaces */
+   for (i = 0; i < ascii_len; i++)
+   ufshcd_remove_non_printable(_ascii[i]);
+
+   memset(buf + QUERY_DESC_HDR_SIZE, 0,
+   size - QUERY_DESC_HDR_SIZE);
+   memcpy(buf + QUERY_DESC_HDR_SIZE, buff_ascii, ascii_len);
+   buf[QUERY_DESC_LENGTH_OFFSET] = ascii_len + QUERY_DESC_HDR_SIZE;
+out_free_buff:
+   kfree(buff_ascii);
+   }
+out:
+   return err;
+}
+EXPORT_SYMBOL(ufshcd_read_string_desc);
+
 /**
  * ufshcd_read_unit_desc_param - read the specified unit descriptor parameter
  * @hba: Pointer to adapter instance
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index a6d3572..54e13cc 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/

[PATCH v8 12/17] scsi: ufs: tune UniPro parameters to optimize hibern8 exit time

2016-03-10 Thread Yaniv Gardi
Optimal values of local UniPro parameters like PA_Hibern8Time &
PA_TActivate can help reduce the hibern8 exit latency. If both host and
device supports UniPro ver1.6 or later, these parameters will be
automatically tuned during link startup itself. But if either host or
device doesn't support UniPro ver 1.6 or later, we have to manually
tune them. But to keep manual tuning logic simple, we will only do
manual tuning if local unipro version doesn't support ver1.6 or later.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 118 ++
 drivers/scsi/ufs/ufshcd.h |   1 +
 drivers/scsi/ufs/ufshci.h |   2 +
 drivers/scsi/ufs/unipro.h |  21 +
 4 files changed, 142 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 82a4b1c..305210f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -570,6 +570,34 @@ static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
 }
 
+u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba)
+{
+   /* HCI version 1.0 and 1.1 supports UniPro 1.41 */
+   if ((hba->ufs_version == UFSHCI_VERSION_10) ||
+   (hba->ufs_version == UFSHCI_VERSION_11))
+   return UFS_UNIPRO_VER_1_41;
+   else
+   return UFS_UNIPRO_VER_1_6;
+}
+EXPORT_SYMBOL(ufshcd_get_local_unipro_ver);
+
+static bool ufshcd_is_unipro_pa_params_tuning_req(struct ufs_hba *hba)
+{
+   /*
+* If both host and device support UniPro ver1.6 or later, PA layer
+* parameters tuning happens during link startup itself.
+*
+* We can manually tune PA layer parameters if either host or device
+* doesn't support UniPro ver 1.6 or later. But to keep manual tuning
+* logic simple, we will only do manual tuning if local unipro version
+* doesn't support ver1.6 or later.
+*/
+   if (ufshcd_get_local_unipro_ver(hba) < UFS_UNIPRO_VER_1_6)
+   return true;
+   else
+   return false;
+}
+
 static void ufshcd_ungate_work(struct work_struct *work)
 {
int ret;
@@ -4852,6 +4880,95 @@ void ufs_advertise_fixup_device(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_tune_pa_tactivate - Tunes PA_TActivate of local UniPro
+ * @hba: per-adapter instance
+ *
+ * PA_TActivate parameter can be tuned manually if UniPro version is less than
+ * 1.61. PA_TActivate needs to be greater than or equal to peerM-PHY's
+ * RX_MIN_ACTIVATETIME_CAPABILITY attribute. This optimal value can help reduce
+ * the hibern8 exit latency.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_tune_pa_tactivate(struct ufs_hba *hba)
+{
+   int ret = 0;
+   u32 peer_rx_min_activatetime = 0, tuned_pa_tactivate;
+
+   ret = ufshcd_dme_peer_get(hba,
+ UIC_ARG_MIB_SEL(
+   RX_MIN_ACTIVATETIME_CAPABILITY,
+   UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
+ _rx_min_activatetime);
+   if (ret)
+   goto out;
+
+   /* make sure proper unit conversion is applied */
+   tuned_pa_tactivate =
+   ((peer_rx_min_activatetime * RX_MIN_ACTIVATETIME_UNIT_US)
+/ PA_TACTIVATE_TIME_UNIT_US);
+   ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE),
+tuned_pa_tactivate);
+
+out:
+   return ret;
+}
+
+/**
+ * ufshcd_tune_pa_hibern8time - Tunes PA_Hibern8Time of local UniPro
+ * @hba: per-adapter instance
+ *
+ * PA_Hibern8Time parameter can be tuned manually if UniPro version is less 
than
+ * 1.61. PA_Hibern8Time needs to be maximum of local M-PHY's
+ * TX_HIBERN8TIME_CAPABILITY & peer M-PHY's RX_HIBERN8TIME_CAPABILITY.
+ * This optimal value can help reduce the hibern8 exit latency.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_tune_pa_hibern8time(struct ufs_hba *hba)
+{
+   int ret = 0;
+   u32 local_tx_hibern8_time_cap = 0, peer_rx_hibern8_time_cap = 0;
+   u32 max_hibern8_time, tuned_pa_hibern8time;
+
+   ret = ufshcd_dme_get(hba,
+UIC_ARG_MIB_SEL(TX_HIBERN8TIME_CAPABILITY,
+   UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
+ _tx_hibern8_time_cap);
+   if (ret)
+   goto out;
+
+   ret = ufshcd_dme_peer_get(hba,
+ UIC_ARG_MIB_SEL(RX_HIBERN8TIME_CAPABILITY,
+   UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
+ _rx_hibern8_time_cap);
+   if (ret)
+   goto out;
+
+   max_hiber

[PATCH v8 06/17] scsi: ufs: separate device and host quirks

2016-03-10 Thread Yaniv Gardi
Currently we use the host quirks mechanism in order to
handle both device and host controller quirks.
In order to support various of UFS devices we should separate
handling the device quirks from the host controller's.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Raviv Shvili <rshv...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs.h|  31 ++
 drivers/scsi/ufs/ufs_quirks.h | 139 ++
 drivers/scsi/ufs/ufshcd.c |  71 +
 drivers/scsi/ufs/ufshcd.h |   3 +
 4 files changed, 244 insertions(+)
 create mode 100644 drivers/scsi/ufs/ufs_quirks.h

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index aacb235..8ec6356 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -196,6 +196,37 @@ enum unit_desc_param {
UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1  = 0x22,
 };
 
+/* Device descriptor parameters offsets in bytes*/
+enum device_desc_param {
+   DEVICE_DESC_PARAM_LEN   = 0x0,
+   DEVICE_DESC_PARAM_TYPE  = 0x1,
+   DEVICE_DESC_PARAM_DEVICE_TYPE   = 0x2,
+   DEVICE_DESC_PARAM_DEVICE_CLASS  = 0x3,
+   DEVICE_DESC_PARAM_DEVICE_SUB_CLASS  = 0x4,
+   DEVICE_DESC_PARAM_PRTCL = 0x5,
+   DEVICE_DESC_PARAM_NUM_LU= 0x6,
+   DEVICE_DESC_PARAM_NUM_WLU   = 0x7,
+   DEVICE_DESC_PARAM_BOOT_ENBL = 0x8,
+   DEVICE_DESC_PARAM_DESC_ACCSS_ENBL   = 0x9,
+   DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA,
+   DEVICE_DESC_PARAM_HIGH_PR_LUN   = 0xB,
+   DEVICE_DESC_PARAM_SEC_RMV_TYPE  = 0xC,
+   DEVICE_DESC_PARAM_SEC_LU= 0xD,
+   DEVICE_DESC_PARAM_BKOP_TERM_LT  = 0xE,
+   DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF,
+   DEVICE_DESC_PARAM_SPEC_VER  = 0x10,
+   DEVICE_DESC_PARAM_MANF_DATE = 0x12,
+   DEVICE_DESC_PARAM_MANF_NAME = 0x14,
+   DEVICE_DESC_PARAM_PRDCT_NAME= 0x15,
+   DEVICE_DESC_PARAM_SN= 0x16,
+   DEVICE_DESC_PARAM_OEM_ID= 0x17,
+   DEVICE_DESC_PARAM_MANF_ID   = 0x18,
+   DEVICE_DESC_PARAM_UD_OFFSET = 0x1A,
+   DEVICE_DESC_PARAM_UD_LEN= 0x1B,
+   DEVICE_DESC_PARAM_RTT_CAP   = 0x1C,
+   DEVICE_DESC_PARAM_FRQ_RTC   = 0x1D,
+};
+
 /*
  * Logical Unit Write Protect
  * 00h: LU not write protected
diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h
new file mode 100644
index 000..62ebd89
--- /dev/null
+++ b/drivers/scsi/ufs/ufs_quirks.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UFS_QUIRKS_H_
+#define _UFS_QUIRKS_H_
+
+/* return true if s1 is a prefix of s2 */
+#define STR_PRFX_EQUAL(s1, s2) !strncmp(s1, s2, strlen(s1))
+
+#define UFS_ANY_VENDOR -1
+#define UFS_ANY_MODEL  "ANY_MODEL"
+
+#define MAX_MODEL_LEN 16
+
+#define UFS_VENDOR_TOSHIBA 0x198
+#define UFS_VENDOR_SAMSUNG 0x1CE
+
+/**
+ * ufs_device_info - ufs device details
+ * @wmanufacturerid: card details
+ * @model: card model
+ */
+struct ufs_device_info {
+   u16 wmanufacturerid;
+   char model[MAX_MODEL_LEN + 1];
+};
+
+/**
+ * ufs_dev_fix - ufs device quirk info
+ * @card: ufs card details
+ * @quirk: device quirk
+ */
+struct ufs_dev_fix {
+   struct ufs_device_info card;
+   unsigned int quirk;
+};
+
+#define END_FIX { { 0 }, 0 }
+
+/* add specific device quirk */
+#define UFS_FIX(_vendor, _model, _quirk) \
+   { \
+   .card.wmanufacturerid = (_vendor),\
+   .card.model = (_model),   \
+   .quirk = (_quirk),\
+   }
+
+/*
+ * If UFS device is having issue in processing LCC (Line Control
+ * Command) coming from UFS host controller then enable this quirk.
+ * When this quirk is enabled, host controller driver should disable
+ * the LCC transmission on UFS host controller (by clearing
+ * TX_LCC_ENABLE attribute of host to 0).
+ */
+#define UFS_DEVICE_QUIRK_BROKEN_LCC (1 << 0)
+
+/*
+ * Some UFS devices don't need VCCQ rail for device operations. Enabling this
+ * quirk for su

[PATCH v8 03/17] scsi: ufs: implement scsi host timeout handler

2016-03-10 Thread Yaniv Gardi
A race condition exists between request requeueing and scsi layer
error handling:
When UFS driver queuecommand returns a busy status for a request,
it will be requeued and its tag will be freed and set to -1.
At the same time it is possible that the request will timeout and
scsi layer will start error handling for it. The scsi layer reuses
the request and its tag to send error related commands to the device,
however its tag is no longer valid.
As this request was never really sent to the device, there is no
point to start error handling with the device.
Implement the scsi error handling timeout callback and bypass SCSI
error handling for request that were not actually sent to the device.
For such requests simply reset the block layer timer. Otherwise, let
SCSI layer perform the usual error handling.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 36 
 1 file changed, 36 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index de7280c..3400ceb 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -4568,6 +4568,41 @@ static void ufshcd_async_scan(void *data, async_cookie_t 
cookie)
ufshcd_probe_hba(hba);
 }
 
+static enum blk_eh_timer_return ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
+{
+   unsigned long flags;
+   struct Scsi_Host *host;
+   struct ufs_hba *hba;
+   int index;
+   bool found = false;
+
+   if (!scmd || !scmd->device || !scmd->device->host)
+   return BLK_EH_NOT_HANDLED;
+
+   host = scmd->device->host;
+   hba = shost_priv(host);
+   if (!hba)
+   return BLK_EH_NOT_HANDLED;
+
+   spin_lock_irqsave(host->host_lock, flags);
+
+   for_each_set_bit(index, >outstanding_reqs, hba->nutrs) {
+   if (hba->lrb[index].cmd == scmd) {
+   found = true;
+   break;
+   }
+   }
+
+   spin_unlock_irqrestore(host->host_lock, flags);
+
+   /*
+* Bypass SCSI error handling and reset the block layer timer if this
+* SCSI command was not actually dispatched to UFS driver, otherwise
+* let SCSI layer handle the error as usual.
+*/
+   return found ? BLK_EH_NOT_HANDLED : BLK_EH_RESET_TIMER;
+}
+
 static struct scsi_host_template ufshcd_driver_template = {
.module = THIS_MODULE,
.name   = UFSHCD,
@@ -4580,6 +4615,7 @@ static struct scsi_host_template ufshcd_driver_template = 
{
.eh_abort_handler   = ufshcd_abort,
.eh_device_reset_handler = ufshcd_eh_device_reset_handler,
.eh_host_reset_handler   = ufshcd_eh_host_reset_handler,
+   .eh_timed_out   = ufshcd_eh_timed_out,
.this_id= -1,
.sg_tablesize   = SG_ALL,
.cmd_per_lun= UFSHCD_CMD_PER_LUN,
-- 
1.8.5.2

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


[PATCH v8 10/17] scsi: ufs: add retry for query descriptors

2016-03-10 Thread Yaniv Gardi
Query commands have 100ms timeout and it may timeout if they are
issued in parallel to ongoing read/write SCSI commands, this change
adds the retry (max: 10) in case command timeouts.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 55 +++
 1 file changed, 37 insertions(+), 18 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 4eedb7f..b429a57 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1875,21 +1875,7 @@ static int ufshcd_query_attr_retry(struct ufs_hba *hba,
return ret;
 }
 
-/**
- * ufshcd_query_descriptor - API function for sending descriptor requests
- * hba: per-adapter instance
- * opcode: attribute opcode
- * idn: attribute idn to access
- * index: index field
- * selector: selector field
- * desc_buf: the buffer that contains the descriptor
- * buf_len: length parameter passed to the device
- *
- * Returns 0 for success, non-zero in case of failure.
- * The buf_len parameter will contain, on return, the length parameter
- * received on the response.
- */
-static int ufshcd_query_descriptor(struct ufs_hba *hba,
+static int __ufshcd_query_descriptor(struct ufs_hba *hba,
enum query_opcode opcode, enum desc_idn idn, u8 index,
u8 selector, u8 *desc_buf, int *buf_len)
 {
@@ -1954,6 +1940,39 @@ out:
 }
 
 /**
+ * ufshcd_query_descriptor_retry - API function for sending descriptor
+ * requests
+ * hba: per-adapter instance
+ * opcode: attribute opcode
+ * idn: attribute idn to access
+ * index: index field
+ * selector: selector field
+ * desc_buf: the buffer that contains the descriptor
+ * buf_len: length parameter passed to the device
+ *
+ * Returns 0 for success, non-zero in case of failure.
+ * The buf_len parameter will contain, on return, the length parameter
+ * received on the response.
+ */
+int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
+   enum query_opcode opcode, enum desc_idn idn, u8 index,
+   u8 selector, u8 *desc_buf, int *buf_len)
+{
+   int err;
+   int retries;
+
+   for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
+   err = __ufshcd_query_descriptor(hba, opcode, idn, index,
+   selector, desc_buf, buf_len);
+   if (!err || err == -EINVAL)
+   break;
+   }
+
+   return err;
+}
+EXPORT_SYMBOL(ufshcd_query_descriptor_retry);
+
+/**
  * ufshcd_read_desc_param - read the specified descriptor parameter
  * @hba: Pointer to adapter instance
  * @desc_id: descriptor idn value
@@ -1995,9 +2014,9 @@ static int ufshcd_read_desc_param(struct ufs_hba *hba,
return -ENOMEM;
}
 
-   ret = ufshcd_query_descriptor(hba, UPIU_QUERY_OPCODE_READ_DESC,
- desc_id, desc_index, 0, desc_buf,
- _len);
+   ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC,
+   desc_id, desc_index, 0, desc_buf,
+   _len);
 
if (ret || (buff_len < ufs_query_desc_max_size[desc_id]) ||
(desc_buf[QUERY_DESC_LENGTH_OFFSET] !=
-- 
1.8.5.2

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


[PATCH v8 07/17] scsi: ufs: disable vccq if it's not needed by UFS device

2016-03-10 Thread Yaniv Gardi
Some UFS devices don't require VCCQ rail for device operations hence
this change adds support to recognize such devices and remove vote for
the unused VCCQ rail.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs.h|  1 +
 drivers/scsi/ufs/ufshcd.c | 60 +++
 2 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 8ec6356..b291fa6 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -501,6 +501,7 @@ struct ufs_vreg {
struct regulator *reg;
const char *name;
bool enabled;
+   bool unused;
int min_uV;
int max_uV;
int min_uA;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index f8b458f..8ed7d4b 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -195,6 +195,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba);
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
 bool skip_ref_clk);
 static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused);
 static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba);
 static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
 static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
@@ -4662,6 +4663,12 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
goto out;
 
ufs_advertise_fixup_device(hba);
+
+   ret = ufshcd_set_vccq_rail_unused(hba,
+   (hba->dev_quirks & UFS_DEVICE_NO_VCCQ) ? true : false);
+   if (ret)
+   goto out;
+
/* UFS device is also active now */
ufshcd_set_ufs_dev_active(hba);
ufshcd_force_reset_auto_bkops(hba);
@@ -4812,13 +4819,24 @@ static int ufshcd_config_vreg_load(struct device *dev, 
struct ufs_vreg *vreg,
 static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg,
+  UFS_VREG_LPM_LOAD_UA);
 }
 
 static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
 }
 
 static int ufshcd_config_vreg(struct device *dev,
@@ -4853,7 +4871,9 @@ static int ufshcd_enable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (vreg->enabled || vreg->unused)
goto out;
 
ret = ufshcd_config_vreg(dev, vreg, true);
@@ -4873,7 +4893,9 @@ static int ufshcd_disable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || !vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (!vreg->enabled || vreg->unused)
goto out;
 
ret = regulator_disable(vreg->reg);
@@ -4979,6 +5001,36 @@ static int ufshcd_init_hba_vreg(struct ufs_hba *hba)
return 0;
 }
 
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused)
+{
+   int ret = 0;
+   struct ufs_vreg_info *info = >vreg_info;
+
+   if (!info)
+   goto out;
+   else if (!info->vccq)
+   goto out;
+
+   if (unused) {
+   /* shut off the rail here */
+   ret = ufshcd_toggle_vreg(hba->dev, info->vccq, false);
+   /*
+* Mark this rail as no longer used, so it doesn't get enabled
+* later by mistake
+*/
+   if (!ret)
+   info->vccq->unused = true;
+   } else {
+   /*
+* rail should have been already enabled hence just make sure
+* that unused flag is cleared.
+*/
+   info->vccq->unused = false;
+   }
+out:
+   return ret;
+}
+
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
bool skip_ref_clk)
 {
-- 
1.8.5.2

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


[PATCH v8 16/17] scsi: ufs-qcom: enable/disable the device ref clock

2016-03-10 Thread Yaniv Gardi
This change enables the device ref clock before changing to HS mode
and disables it if entered to PWM mode.

Reviewed-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs-qcom.c | 12 
 drivers/scsi/ufs/ufshcd.h   |  9 +
 2 files changed, 21 insertions(+)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 966bacf..849fcf3 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -980,6 +980,10 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
goto out;
}
 
+   /* enable the device ref clock before changing to HS mode */
+   if (!ufshcd_is_hs_mode(>pwr_info) &&
+   ufshcd_is_hs_mode(dev_req_params))
+   ufs_qcom_dev_ref_clk_ctrl(host, true);
break;
case POST_CHANGE:
if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx,
@@ -1007,6 +1011,11 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba 
*hba,
memcpy(>dev_req_params,
dev_req_params, sizeof(*dev_req_params));
ufs_qcom_update_bus_bw_vote(host);
+
+   /* disable the device ref clock if entered PWM mode */
+   if (ufshcd_is_hs_mode(>pwr_info) &&
+   !ufshcd_is_hs_mode(dev_req_params))
+   ufs_qcom_dev_ref_clk_ctrl(host, false);
break;
default:
ret = -EINVAL;
@@ -1108,6 +1117,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, 
bool on)
ufs_qcom_phy_disable_iface_clk(host->generic_phy);
goto out;
}
+   /* enable the device ref clock for HS mode*/
+   if (ufshcd_is_hs_mode(>pwr_info))
+   ufs_qcom_dev_ref_clk_ctrl(host, true);
vote = host->bus_vote.saved_vote;
if (vote == host->bus_vote.min_bw_vote)
ufs_qcom_update_bus_bw_vote(host);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 41d9bfd..4bb6566 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -54,6 +54,7 @@
 #include 
 #include 
 #include 
+#include "unipro.h"
 
 #include 
 #include 
@@ -689,6 +690,14 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
 
 int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size);
 
+static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info)
+{
+   return (pwr_info->pwr_rx == FAST_MODE ||
+   pwr_info->pwr_rx == FASTAUTO_MODE) &&
+   (pwr_info->pwr_tx == FAST_MODE ||
+   pwr_info->pwr_tx == FASTAUTO_MODE);
+}
+
 #define ASCII_STD true
 
 int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
-- 
1.8.5.2

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


[PATCH v8 05/17] scsi: ufs: add support to read device and string descriptors

2016-03-10 Thread Yaniv Gardi
This change adds support to read device descriptor and string descriptor
from a UFS device

Reviewed-by: Gilad Broner 
Reviewed-by: Hannes Reinecke 
Signed-off-by: Raviv Shvili 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs.h|  1 +
 drivers/scsi/ufs/ufshcd.c | 88 ++-
 drivers/scsi/ufs/ufshcd.h |  7 
 3 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 54a16ce..aacb235 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -43,6 +43,7 @@
 #define GENERAL_UPIU_REQUEST_SIZE 32
 #define QUERY_DESC_MAX_SIZE   255
 #define QUERY_DESC_MIN_SIZE   2
+#define QUERY_DESC_HDR_SIZE   2
 #define QUERY_OSF_SIZE(GENERAL_UPIU_REQUEST_SIZE - \
(sizeof(struct utp_upiu_header)))
 
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 80031e6..e2ed415 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -39,7 +39,7 @@
 
 #include 
 #include 
-
+#include 
 #include 
 #include "ufshcd.h"
 #include "unipro.h"
@@ -232,6 +232,16 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
}
 }
 
+/* replace non-printable or non-ASCII characters with spaces */
+static inline void ufshcd_remove_non_printable(char *val)
+{
+   if (!val)
+   return;
+
+   if (*val < 0x20 || *val > 0x7e)
+   *val = ' ';
+}
+
 /*
  * ufshcd_wait_for_register - wait for register value to change
  * @hba - per-adapter interface
@@ -2021,6 +2031,82 @@ static inline int ufshcd_read_power_desc(struct ufs_hba 
*hba,
return ufshcd_read_desc(hba, QUERY_DESC_IDN_POWER, 0, buf, size);
 }
 
+int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size)
+{
+   return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, buf, size);
+}
+EXPORT_SYMBOL(ufshcd_read_device_desc);
+
+/**
+ * ufshcd_read_string_desc - read string descriptor
+ * @hba: pointer to adapter instance
+ * @desc_index: descriptor index
+ * @buf: pointer to buffer where descriptor would be read
+ * @size: size of buf
+ * @ascii: if true convert from unicode to ascii characters
+ *
+ * Return 0 in case of success, non-zero otherwise
+ */
+int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
+   u32 size, bool ascii)
+{
+   int err = 0;
+
+   err = ufshcd_read_desc(hba,
+   QUERY_DESC_IDN_STRING, desc_index, buf, size);
+
+   if (err) {
+   dev_err(hba->dev, "%s: reading String Desc failed after %d 
retries. err = %d\n",
+   __func__, QUERY_REQ_RETRIES, err);
+   goto out;
+   }
+
+   if (ascii) {
+   int desc_len;
+   int ascii_len;
+   int i;
+   char *buff_ascii;
+
+   desc_len = buf[0];
+   /* remove header and divide by 2 to move from UTF16 to UTF8 */
+   ascii_len = (desc_len - QUERY_DESC_HDR_SIZE) / 2 + 1;
+   if (size < ascii_len + QUERY_DESC_HDR_SIZE) {
+   dev_err(hba->dev, "%s: buffer allocated size is too 
small\n",
+   __func__);
+   err = -ENOMEM;
+   goto out;
+   }
+
+   buff_ascii = kmalloc(ascii_len, GFP_KERNEL);
+   if (!buff_ascii) {
+   err = -ENOMEM;
+   goto out_free_buff;
+   }
+
+   /*
+* the descriptor contains string in UTF16 format
+* we need to convert to utf-8 so it can be displayed
+*/
+   utf16s_to_utf8s((wchar_t *)[QUERY_DESC_HDR_SIZE],
+   desc_len - QUERY_DESC_HDR_SIZE,
+   UTF16_BIG_ENDIAN, buff_ascii, ascii_len);
+
+   /* replace non-printable or non-ASCII characters with spaces */
+   for (i = 0; i < ascii_len; i++)
+   ufshcd_remove_non_printable(_ascii[i]);
+
+   memset(buf + QUERY_DESC_HDR_SIZE, 0,
+   size - QUERY_DESC_HDR_SIZE);
+   memcpy(buf + QUERY_DESC_HDR_SIZE, buff_ascii, ascii_len);
+   buf[QUERY_DESC_LENGTH_OFFSET] = ascii_len + QUERY_DESC_HDR_SIZE;
+out_free_buff:
+   kfree(buff_ascii);
+   }
+out:
+   return err;
+}
+EXPORT_SYMBOL(ufshcd_read_string_desc);
+
 /**
  * ufshcd_read_unit_desc_param - read the specified unit descriptor parameter
  * @hba: Pointer to adapter instance
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index a6d3572..54e13cc 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -678,6 +678,13 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
return ufshcd

[PATCH v8 12/17] scsi: ufs: tune UniPro parameters to optimize hibern8 exit time

2016-03-10 Thread Yaniv Gardi
Optimal values of local UniPro parameters like PA_Hibern8Time &
PA_TActivate can help reduce the hibern8 exit latency. If both host and
device supports UniPro ver1.6 or later, these parameters will be
automatically tuned during link startup itself. But if either host or
device doesn't support UniPro ver 1.6 or later, we have to manually
tune them. But to keep manual tuning logic simple, we will only do
manual tuning if local unipro version doesn't support ver1.6 or later.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 118 ++
 drivers/scsi/ufs/ufshcd.h |   1 +
 drivers/scsi/ufs/ufshci.h |   2 +
 drivers/scsi/ufs/unipro.h |  21 +
 4 files changed, 142 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 82a4b1c..305210f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -570,6 +570,34 @@ static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
 }
 
+u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba)
+{
+   /* HCI version 1.0 and 1.1 supports UniPro 1.41 */
+   if ((hba->ufs_version == UFSHCI_VERSION_10) ||
+   (hba->ufs_version == UFSHCI_VERSION_11))
+   return UFS_UNIPRO_VER_1_41;
+   else
+   return UFS_UNIPRO_VER_1_6;
+}
+EXPORT_SYMBOL(ufshcd_get_local_unipro_ver);
+
+static bool ufshcd_is_unipro_pa_params_tuning_req(struct ufs_hba *hba)
+{
+   /*
+* If both host and device support UniPro ver1.6 or later, PA layer
+* parameters tuning happens during link startup itself.
+*
+* We can manually tune PA layer parameters if either host or device
+* doesn't support UniPro ver 1.6 or later. But to keep manual tuning
+* logic simple, we will only do manual tuning if local unipro version
+* doesn't support ver1.6 or later.
+*/
+   if (ufshcd_get_local_unipro_ver(hba) < UFS_UNIPRO_VER_1_6)
+   return true;
+   else
+   return false;
+}
+
 static void ufshcd_ungate_work(struct work_struct *work)
 {
int ret;
@@ -4852,6 +4880,95 @@ void ufs_advertise_fixup_device(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_tune_pa_tactivate - Tunes PA_TActivate of local UniPro
+ * @hba: per-adapter instance
+ *
+ * PA_TActivate parameter can be tuned manually if UniPro version is less than
+ * 1.61. PA_TActivate needs to be greater than or equal to peerM-PHY's
+ * RX_MIN_ACTIVATETIME_CAPABILITY attribute. This optimal value can help reduce
+ * the hibern8 exit latency.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_tune_pa_tactivate(struct ufs_hba *hba)
+{
+   int ret = 0;
+   u32 peer_rx_min_activatetime = 0, tuned_pa_tactivate;
+
+   ret = ufshcd_dme_peer_get(hba,
+ UIC_ARG_MIB_SEL(
+   RX_MIN_ACTIVATETIME_CAPABILITY,
+   UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
+ _rx_min_activatetime);
+   if (ret)
+   goto out;
+
+   /* make sure proper unit conversion is applied */
+   tuned_pa_tactivate =
+   ((peer_rx_min_activatetime * RX_MIN_ACTIVATETIME_UNIT_US)
+/ PA_TACTIVATE_TIME_UNIT_US);
+   ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE),
+tuned_pa_tactivate);
+
+out:
+   return ret;
+}
+
+/**
+ * ufshcd_tune_pa_hibern8time - Tunes PA_Hibern8Time of local UniPro
+ * @hba: per-adapter instance
+ *
+ * PA_Hibern8Time parameter can be tuned manually if UniPro version is less 
than
+ * 1.61. PA_Hibern8Time needs to be maximum of local M-PHY's
+ * TX_HIBERN8TIME_CAPABILITY & peer M-PHY's RX_HIBERN8TIME_CAPABILITY.
+ * This optimal value can help reduce the hibern8 exit latency.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_tune_pa_hibern8time(struct ufs_hba *hba)
+{
+   int ret = 0;
+   u32 local_tx_hibern8_time_cap = 0, peer_rx_hibern8_time_cap = 0;
+   u32 max_hibern8_time, tuned_pa_hibern8time;
+
+   ret = ufshcd_dme_get(hba,
+UIC_ARG_MIB_SEL(TX_HIBERN8TIME_CAPABILITY,
+   UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
+ _tx_hibern8_time_cap);
+   if (ret)
+   goto out;
+
+   ret = ufshcd_dme_peer_get(hba,
+ UIC_ARG_MIB_SEL(RX_HIBERN8TIME_CAPABILITY,
+   UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
+ _rx_hibern8_time_cap);
+   if (ret)
+   goto out;
+
+   max_hibern8_time = max(local_tx_hibern8_time_cap,
+  peer_rx_hibern8_time_cap);

[PATCH v8 06/17] scsi: ufs: separate device and host quirks

2016-03-10 Thread Yaniv Gardi
Currently we use the host quirks mechanism in order to
handle both device and host controller quirks.
In order to support various of UFS devices we should separate
handling the device quirks from the host controller's.

Reviewed-by: Gilad Broner 
Reviewed-by: Hannes Reinecke 
Signed-off-by: Raviv Shvili 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs.h|  31 ++
 drivers/scsi/ufs/ufs_quirks.h | 139 ++
 drivers/scsi/ufs/ufshcd.c |  71 +
 drivers/scsi/ufs/ufshcd.h |   3 +
 4 files changed, 244 insertions(+)
 create mode 100644 drivers/scsi/ufs/ufs_quirks.h

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index aacb235..8ec6356 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -196,6 +196,37 @@ enum unit_desc_param {
UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1  = 0x22,
 };
 
+/* Device descriptor parameters offsets in bytes*/
+enum device_desc_param {
+   DEVICE_DESC_PARAM_LEN   = 0x0,
+   DEVICE_DESC_PARAM_TYPE  = 0x1,
+   DEVICE_DESC_PARAM_DEVICE_TYPE   = 0x2,
+   DEVICE_DESC_PARAM_DEVICE_CLASS  = 0x3,
+   DEVICE_DESC_PARAM_DEVICE_SUB_CLASS  = 0x4,
+   DEVICE_DESC_PARAM_PRTCL = 0x5,
+   DEVICE_DESC_PARAM_NUM_LU= 0x6,
+   DEVICE_DESC_PARAM_NUM_WLU   = 0x7,
+   DEVICE_DESC_PARAM_BOOT_ENBL = 0x8,
+   DEVICE_DESC_PARAM_DESC_ACCSS_ENBL   = 0x9,
+   DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA,
+   DEVICE_DESC_PARAM_HIGH_PR_LUN   = 0xB,
+   DEVICE_DESC_PARAM_SEC_RMV_TYPE  = 0xC,
+   DEVICE_DESC_PARAM_SEC_LU= 0xD,
+   DEVICE_DESC_PARAM_BKOP_TERM_LT  = 0xE,
+   DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF,
+   DEVICE_DESC_PARAM_SPEC_VER  = 0x10,
+   DEVICE_DESC_PARAM_MANF_DATE = 0x12,
+   DEVICE_DESC_PARAM_MANF_NAME = 0x14,
+   DEVICE_DESC_PARAM_PRDCT_NAME= 0x15,
+   DEVICE_DESC_PARAM_SN= 0x16,
+   DEVICE_DESC_PARAM_OEM_ID= 0x17,
+   DEVICE_DESC_PARAM_MANF_ID   = 0x18,
+   DEVICE_DESC_PARAM_UD_OFFSET = 0x1A,
+   DEVICE_DESC_PARAM_UD_LEN= 0x1B,
+   DEVICE_DESC_PARAM_RTT_CAP   = 0x1C,
+   DEVICE_DESC_PARAM_FRQ_RTC   = 0x1D,
+};
+
 /*
  * Logical Unit Write Protect
  * 00h: LU not write protected
diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h
new file mode 100644
index 000..62ebd89
--- /dev/null
+++ b/drivers/scsi/ufs/ufs_quirks.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UFS_QUIRKS_H_
+#define _UFS_QUIRKS_H_
+
+/* return true if s1 is a prefix of s2 */
+#define STR_PRFX_EQUAL(s1, s2) !strncmp(s1, s2, strlen(s1))
+
+#define UFS_ANY_VENDOR -1
+#define UFS_ANY_MODEL  "ANY_MODEL"
+
+#define MAX_MODEL_LEN 16
+
+#define UFS_VENDOR_TOSHIBA 0x198
+#define UFS_VENDOR_SAMSUNG 0x1CE
+
+/**
+ * ufs_device_info - ufs device details
+ * @wmanufacturerid: card details
+ * @model: card model
+ */
+struct ufs_device_info {
+   u16 wmanufacturerid;
+   char model[MAX_MODEL_LEN + 1];
+};
+
+/**
+ * ufs_dev_fix - ufs device quirk info
+ * @card: ufs card details
+ * @quirk: device quirk
+ */
+struct ufs_dev_fix {
+   struct ufs_device_info card;
+   unsigned int quirk;
+};
+
+#define END_FIX { { 0 }, 0 }
+
+/* add specific device quirk */
+#define UFS_FIX(_vendor, _model, _quirk) \
+   { \
+   .card.wmanufacturerid = (_vendor),\
+   .card.model = (_model),   \
+   .quirk = (_quirk),\
+   }
+
+/*
+ * If UFS device is having issue in processing LCC (Line Control
+ * Command) coming from UFS host controller then enable this quirk.
+ * When this quirk is enabled, host controller driver should disable
+ * the LCC transmission on UFS host controller (by clearing
+ * TX_LCC_ENABLE attribute of host to 0).
+ */
+#define UFS_DEVICE_QUIRK_BROKEN_LCC (1 << 0)
+
+/*
+ * Some UFS devices don't need VCCQ rail for device operations. Enabling this
+ * quirk for such devices will make sure that VCCQ rail is not voted.
+ */
+#define UFS_DEVICE_NO_VCCQ (1 << 1)
+
+/*
+ * Some ven

[PATCH v8 03/17] scsi: ufs: implement scsi host timeout handler

2016-03-10 Thread Yaniv Gardi
A race condition exists between request requeueing and scsi layer
error handling:
When UFS driver queuecommand returns a busy status for a request,
it will be requeued and its tag will be freed and set to -1.
At the same time it is possible that the request will timeout and
scsi layer will start error handling for it. The scsi layer reuses
the request and its tag to send error related commands to the device,
however its tag is no longer valid.
As this request was never really sent to the device, there is no
point to start error handling with the device.
Implement the scsi error handling timeout callback and bypass SCSI
error handling for request that were not actually sent to the device.
For such requests simply reset the block layer timer. Otherwise, let
SCSI layer perform the usual error handling.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 36 
 1 file changed, 36 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index de7280c..3400ceb 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -4568,6 +4568,41 @@ static void ufshcd_async_scan(void *data, async_cookie_t 
cookie)
ufshcd_probe_hba(hba);
 }
 
+static enum blk_eh_timer_return ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
+{
+   unsigned long flags;
+   struct Scsi_Host *host;
+   struct ufs_hba *hba;
+   int index;
+   bool found = false;
+
+   if (!scmd || !scmd->device || !scmd->device->host)
+   return BLK_EH_NOT_HANDLED;
+
+   host = scmd->device->host;
+   hba = shost_priv(host);
+   if (!hba)
+   return BLK_EH_NOT_HANDLED;
+
+   spin_lock_irqsave(host->host_lock, flags);
+
+   for_each_set_bit(index, >outstanding_reqs, hba->nutrs) {
+   if (hba->lrb[index].cmd == scmd) {
+   found = true;
+   break;
+   }
+   }
+
+   spin_unlock_irqrestore(host->host_lock, flags);
+
+   /*
+* Bypass SCSI error handling and reset the block layer timer if this
+* SCSI command was not actually dispatched to UFS driver, otherwise
+* let SCSI layer handle the error as usual.
+*/
+   return found ? BLK_EH_NOT_HANDLED : BLK_EH_RESET_TIMER;
+}
+
 static struct scsi_host_template ufshcd_driver_template = {
.module = THIS_MODULE,
.name   = UFSHCD,
@@ -4580,6 +4615,7 @@ static struct scsi_host_template ufshcd_driver_template = 
{
.eh_abort_handler   = ufshcd_abort,
.eh_device_reset_handler = ufshcd_eh_device_reset_handler,
.eh_host_reset_handler   = ufshcd_eh_host_reset_handler,
+   .eh_timed_out   = ufshcd_eh_timed_out,
.this_id= -1,
.sg_tablesize   = SG_ALL,
.cmd_per_lun= UFSHCD_CMD_PER_LUN,
-- 
1.8.5.2

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


[PATCH v8 09/17] scsi: ufs: add error recovery after DL NAC error

2016-03-10 Thread Yaniv Gardi
Some vendor's UFS device sends back to back NACs for the DL data frames
causing the host controller to raise the DFES error status. Sometimes
such UFS devices send back to back NAC without waiting for new
retransmitted DL frame from the host and in such cases it might be
possible the Host UniPro goes into bad state without raising the DFES
error interrupt. If this happens then all the pending commands would
timeout only after respective SW command (which is generally too
large).

This change workarounds such device behaviour like this:
- As soon as SW sees the DL NAC error, it would schedule the error
  handler
- Error handler would sleep for 50ms to see if there any fatal errors
  raised by UFS controller.
   - If there are fatal errors then SW does normal error recovery.
   - If there are no fatal errors then SW sends the NOP command to
 device to check if link is alive.
   - If NOP command times out, SW does normal error recovery
   - If NOP command succeed, skip the error handling.

If DL NAC error is seen multiple times with some vendor's UFS devices
then enable this quirk to initiate quick error recovery and also
silence related error logs to reduce spamming of kernel logs.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 93 +++
 drivers/scsi/ufs/ufshci.h |  2 +
 2 files changed, 95 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index adaae34..4eedb7f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3795,6 +3795,79 @@ static void ufshcd_complete_requests(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_quirk_dl_nac_errors - This function checks if error handling is
+ * to recover from the DL NAC errors or not.
+ * @hba: per-adapter instance
+ *
+ * Returns true if error handling is required, false otherwise
+ */
+static bool ufshcd_quirk_dl_nac_errors(struct ufs_hba *hba)
+{
+   unsigned long flags;
+   bool err_handling = true;
+
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   /*
+* UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS only workaround the
+* device fatal error and/or DL NAC & REPLAY timeout errors.
+*/
+   if (hba->saved_err & (CONTROLLER_FATAL_ERROR | SYSTEM_BUS_FATAL_ERROR))
+   goto out;
+
+   if ((hba->saved_err & DEVICE_FATAL_ERROR) ||
+   ((hba->saved_err & UIC_ERROR) &&
+(hba->saved_uic_err & UFSHCD_UIC_DL_TCx_REPLAY_ERROR)))
+   goto out;
+
+   if ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)) {
+   int err;
+   /*
+* wait for 50ms to see if we can get any other errors or not.
+*/
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   msleep(50);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   /*
+* now check if we have got any other severe errors other than
+* DL NAC error?
+*/
+   if ((hba->saved_err & INT_FATAL_ERRORS) ||
+   ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)))
+   goto out;
+
+   /*
+* As DL NAC is the only error received so far, send out NOP
+* command to confirm if link is still active or not.
+*   - If we don't get any response then do error recovery.
+*   - If we get response then clear the DL NAC error bit.
+*/
+
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   err = ufshcd_verify_dev_init(hba);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   if (err)
+   goto out;
+
+   /* Link seems to be alive hence ignore the DL NAC errors */
+   if (hba->saved_uic_err == UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)
+   hba->saved_err &= ~UIC_ERROR;
+   /* clear NAC error */
+   hba->saved_uic_err &= ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR;
+   if (!hba->saved_uic_err) {
+   err_handling = false;
+   goto out;
+   }
+   }
+out:
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   return err_handling;
+}
+
+/**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
  */
@@ -3822,6 +3895,17 @@ static void ufshcd_err_handle

[PATCH v8 09/17] scsi: ufs: add error recovery after DL NAC error

2016-03-10 Thread Yaniv Gardi
Some vendor's UFS device sends back to back NACs for the DL data frames
causing the host controller to raise the DFES error status. Sometimes
such UFS devices send back to back NAC without waiting for new
retransmitted DL frame from the host and in such cases it might be
possible the Host UniPro goes into bad state without raising the DFES
error interrupt. If this happens then all the pending commands would
timeout only after respective SW command (which is generally too
large).

This change workarounds such device behaviour like this:
- As soon as SW sees the DL NAC error, it would schedule the error
  handler
- Error handler would sleep for 50ms to see if there any fatal errors
  raised by UFS controller.
   - If there are fatal errors then SW does normal error recovery.
   - If there are no fatal errors then SW sends the NOP command to
 device to check if link is alive.
   - If NOP command times out, SW does normal error recovery
   - If NOP command succeed, skip the error handling.

If DL NAC error is seen multiple times with some vendor's UFS devices
then enable this quirk to initiate quick error recovery and also
silence related error logs to reduce spamming of kernel logs.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 93 +++
 drivers/scsi/ufs/ufshci.h |  2 +
 2 files changed, 95 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index adaae34..4eedb7f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3795,6 +3795,79 @@ static void ufshcd_complete_requests(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_quirk_dl_nac_errors - This function checks if error handling is
+ * to recover from the DL NAC errors or not.
+ * @hba: per-adapter instance
+ *
+ * Returns true if error handling is required, false otherwise
+ */
+static bool ufshcd_quirk_dl_nac_errors(struct ufs_hba *hba)
+{
+   unsigned long flags;
+   bool err_handling = true;
+
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   /*
+* UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS only workaround the
+* device fatal error and/or DL NAC & REPLAY timeout errors.
+*/
+   if (hba->saved_err & (CONTROLLER_FATAL_ERROR | SYSTEM_BUS_FATAL_ERROR))
+   goto out;
+
+   if ((hba->saved_err & DEVICE_FATAL_ERROR) ||
+   ((hba->saved_err & UIC_ERROR) &&
+(hba->saved_uic_err & UFSHCD_UIC_DL_TCx_REPLAY_ERROR)))
+   goto out;
+
+   if ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)) {
+   int err;
+   /*
+* wait for 50ms to see if we can get any other errors or not.
+*/
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   msleep(50);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   /*
+* now check if we have got any other severe errors other than
+* DL NAC error?
+*/
+   if ((hba->saved_err & INT_FATAL_ERRORS) ||
+   ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)))
+   goto out;
+
+   /*
+* As DL NAC is the only error received so far, send out NOP
+* command to confirm if link is still active or not.
+*   - If we don't get any response then do error recovery.
+*   - If we get response then clear the DL NAC error bit.
+*/
+
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   err = ufshcd_verify_dev_init(hba);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   if (err)
+   goto out;
+
+   /* Link seems to be alive hence ignore the DL NAC errors */
+   if (hba->saved_uic_err == UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)
+   hba->saved_err &= ~UIC_ERROR;
+   /* clear NAC error */
+   hba->saved_uic_err &= ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR;
+   if (!hba->saved_uic_err) {
+   err_handling = false;
+   goto out;
+   }
+   }
+out:
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   return err_handling;
+}
+
+/**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
  */
@@ -3822,6 +3895,17 @@ static void ufshcd_err_handler(struct work_struct *work)
 
/* Complete requests that have door-bell cleared by h/w

[PATCH v8 17/17] scsi: ufs-qcom: add printouts of testbus debug registers

2016-03-10 Thread Yaniv Gardi
This change adds printouts of testbus and debug registers.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs-qcom.c | 77 +
 drivers/scsi/ufs/ufs-qcom.h |  9 ++
 2 files changed, 86 insertions(+)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 849fcf3..3aedf73 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -58,6 +58,12 @@ static void ufs_qcom_dump_regs(struct ufs_hba *hba, int 
offset, int len,
len * 4, false);
 }
 
+static void ufs_qcom_dump_regs_wrapper(struct ufs_hba *hba, int offset, int 
len,
+   char *prefix, void *priv)
+{
+   ufs_qcom_dump_regs(hba, offset, len, prefix);
+}
+
 static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes)
 {
int err = 0;
@@ -1397,6 +1403,74 @@ out:
return err;
 }
 
+static void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba,
+   void *priv, void (*print_fn)(struct ufs_hba *hba,
+   int offset, int num_regs, char *str, void *priv))
+{
+   u32 reg;
+   struct ufs_qcom_host *host;
+
+   if (unlikely(!hba)) {
+   pr_err("%s: hba is NULL\n", __func__);
+   return;
+   }
+   if (unlikely(!print_fn)) {
+   dev_err(hba->dev, "%s: print_fn is NULL\n", __func__);
+   return;
+   }
+
+   host = ufshcd_get_variant(hba);
+   if (!(host->dbg_print_en & UFS_QCOM_DBG_PRINT_REGS_EN))
+   return;
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_REG_OCSC);
+   print_fn(hba, reg, 44, "UFS_UFS_DBG_RD_REG_OCSC ", priv);
+
+   reg = ufshcd_readl(hba, REG_UFS_CFG1);
+   reg |= UFS_BIT(17);
+   ufshcd_writel(hba, reg, REG_UFS_CFG1);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_EDTL_RAM);
+   print_fn(hba, reg, 32, "UFS_UFS_DBG_RD_EDTL_RAM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_DESC_RAM);
+   print_fn(hba, reg, 128, "UFS_UFS_DBG_RD_DESC_RAM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_PRDT_RAM);
+   print_fn(hba, reg, 64, "UFS_UFS_DBG_RD_PRDT_RAM ", priv);
+
+   ufshcd_writel(hba, (reg & ~UFS_BIT(17)), REG_UFS_CFG1);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UAWM);
+   print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UAWM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UARM);
+   print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UARM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TXUC);
+   print_fn(hba, reg, 48, "UFS_DBG_RD_REG_TXUC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_RXUC);
+   print_fn(hba, reg, 27, "UFS_DBG_RD_REG_RXUC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_DFC);
+   print_fn(hba, reg, 19, "UFS_DBG_RD_REG_DFC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TRLUT);
+   print_fn(hba, reg, 34, "UFS_DBG_RD_REG_TRLUT ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TMRLUT);
+   print_fn(hba, reg, 9, "UFS_DBG_RD_REG_TMRLUT ", priv);
+}
+
+static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host)
+{
+   if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
+   ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1);
+   else
+   ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1);
+}
+
 static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host)
 {
/* provide a legal default configuration */
@@ -1505,6 +1579,7 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
ufshcd_rmwl(host->hba, mask,
(u32)host->testbus.select_minor << offset,
reg);
+   ufs_qcom_enable_test_bus(host);
ufshcd_release(host->hba);
pm_runtime_put_sync(host->hba->dev);
 
@@ -1521,8 +1596,10 @@ static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16,
"HCI Vendor Specific Registers ");
 
+   ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper);
ufs_qcom_testbus_read(hba);
 }
+
 /**
  * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
  *
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 36249b3..a19307a 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -241,6 +241,15 @@ struct ufs_qcom_host {
struct ufs_qcom_testbus testbus;
 };
 
+static inline u32
+ufs_qcom_get_debug_

[PATCH v8 17/17] scsi: ufs-qcom: add printouts of testbus debug registers

2016-03-10 Thread Yaniv Gardi
This change adds printouts of testbus and debug registers.

Reviewed-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs-qcom.c | 77 +
 drivers/scsi/ufs/ufs-qcom.h |  9 ++
 2 files changed, 86 insertions(+)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 849fcf3..3aedf73 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -58,6 +58,12 @@ static void ufs_qcom_dump_regs(struct ufs_hba *hba, int 
offset, int len,
len * 4, false);
 }
 
+static void ufs_qcom_dump_regs_wrapper(struct ufs_hba *hba, int offset, int 
len,
+   char *prefix, void *priv)
+{
+   ufs_qcom_dump_regs(hba, offset, len, prefix);
+}
+
 static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes)
 {
int err = 0;
@@ -1397,6 +1403,74 @@ out:
return err;
 }
 
+static void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba,
+   void *priv, void (*print_fn)(struct ufs_hba *hba,
+   int offset, int num_regs, char *str, void *priv))
+{
+   u32 reg;
+   struct ufs_qcom_host *host;
+
+   if (unlikely(!hba)) {
+   pr_err("%s: hba is NULL\n", __func__);
+   return;
+   }
+   if (unlikely(!print_fn)) {
+   dev_err(hba->dev, "%s: print_fn is NULL\n", __func__);
+   return;
+   }
+
+   host = ufshcd_get_variant(hba);
+   if (!(host->dbg_print_en & UFS_QCOM_DBG_PRINT_REGS_EN))
+   return;
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_REG_OCSC);
+   print_fn(hba, reg, 44, "UFS_UFS_DBG_RD_REG_OCSC ", priv);
+
+   reg = ufshcd_readl(hba, REG_UFS_CFG1);
+   reg |= UFS_BIT(17);
+   ufshcd_writel(hba, reg, REG_UFS_CFG1);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_EDTL_RAM);
+   print_fn(hba, reg, 32, "UFS_UFS_DBG_RD_EDTL_RAM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_DESC_RAM);
+   print_fn(hba, reg, 128, "UFS_UFS_DBG_RD_DESC_RAM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_PRDT_RAM);
+   print_fn(hba, reg, 64, "UFS_UFS_DBG_RD_PRDT_RAM ", priv);
+
+   ufshcd_writel(hba, (reg & ~UFS_BIT(17)), REG_UFS_CFG1);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UAWM);
+   print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UAWM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UARM);
+   print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UARM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TXUC);
+   print_fn(hba, reg, 48, "UFS_DBG_RD_REG_TXUC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_RXUC);
+   print_fn(hba, reg, 27, "UFS_DBG_RD_REG_RXUC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_DFC);
+   print_fn(hba, reg, 19, "UFS_DBG_RD_REG_DFC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TRLUT);
+   print_fn(hba, reg, 34, "UFS_DBG_RD_REG_TRLUT ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TMRLUT);
+   print_fn(hba, reg, 9, "UFS_DBG_RD_REG_TMRLUT ", priv);
+}
+
+static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host)
+{
+   if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
+   ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1);
+   else
+   ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1);
+}
+
 static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host)
 {
/* provide a legal default configuration */
@@ -1505,6 +1579,7 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
ufshcd_rmwl(host->hba, mask,
(u32)host->testbus.select_minor << offset,
reg);
+   ufs_qcom_enable_test_bus(host);
ufshcd_release(host->hba);
pm_runtime_put_sync(host->hba->dev);
 
@@ -1521,8 +1596,10 @@ static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16,
"HCI Vendor Specific Registers ");
 
+   ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper);
ufs_qcom_testbus_read(hba);
 }
+
 /**
  * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
  *
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 36249b3..a19307a 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -241,6 +241,15 @@ struct ufs_qcom_host {
struct ufs_qcom_testbus testbus;
 };
 
+static inline u32
+ufs_qcom_get_debug_reg_offset(struct ufs_qcom_host *host, u32 reg)
+{
+  

[PATCH v8 02/17] scsi: ufs: avoid spurious UFS host controller interrupts

2016-03-10 Thread Yaniv Gardi
When control reaches to Linux UFS driver during UFS boot mode, UFS host
controller interrupt status/enable registers may have left over
settings.
In order to avoid any spurious interrupts due to these left overs,
it's important to clear these interrupt status/enable registers before
enabling UFS interrupt handling.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 15 +++
 1 file changed, 15 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a8e42df..de7280c 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5837,6 +5837,21 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem 
*mmio_base, unsigned int irq)
init_waitqueue_head(>dev_cmd.tag_wq);
 
ufshcd_init_clk_gating(hba);
+
+   /*
+* In order to avoid any spurious interrupt immediately after
+* registering UFS controller interrupt handler, clear any pending UFS
+* interrupt status and disable all the UFS interrupts.
+*/
+   ufshcd_writel(hba, ufshcd_readl(hba, REG_INTERRUPT_STATUS),
+ REG_INTERRUPT_STATUS);
+   ufshcd_writel(hba, 0, REG_INTERRUPT_ENABLE);
+   /*
+* Make sure that UFS interrupts are disabled and any pending interrupt
+* status is cleared before registering UFS interrupt handler.
+*/
+   mb();
+
/* IRQ registration */
err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
if (err) {
-- 
1.8.5.2

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


[PATCH v8 11/17] scsi: ufs: handle non spec compliant bkops behaviour by device

2016-03-10 Thread Yaniv Gardi
We are seeing that some devices are raising the urgent bkops exception
events even when BKOPS status doesn't indicate performace impacted or
critical. Handle these device by determining their urgent bkops status
at runtime.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h |  6 ++
 2 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index b429a57..82a4b1c 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3763,7 +3763,7 @@ out:
  */
 static int ufshcd_urgent_bkops(struct ufs_hba *hba)
 {
-   return ufshcd_bkops_ctrl(hba, BKOPS_STATUS_PERF_IMPACT);
+   return ufshcd_bkops_ctrl(hba, hba->urgent_bkops_lvl);
 }
 
 static inline int ufshcd_get_ee_status(struct ufs_hba *hba, u32 *status)
@@ -3772,6 +3772,43 @@ static inline int ufshcd_get_ee_status(struct ufs_hba 
*hba, u32 *status)
QUERY_ATTR_IDN_EE_STATUS, 0, 0, status);
 }
 
+static void ufshcd_bkops_exception_event_handler(struct ufs_hba *hba)
+{
+   int err;
+   u32 curr_status = 0;
+
+   if (hba->is_urgent_bkops_lvl_checked)
+   goto enable_auto_bkops;
+
+   err = ufshcd_get_bkops_status(hba, _status);
+   if (err) {
+   dev_err(hba->dev, "%s: failed to get BKOPS status %d\n",
+   __func__, err);
+   goto out;
+   }
+
+   /*
+* We are seeing that some devices are raising the urgent bkops
+* exception events even when BKOPS status doesn't indicate performace
+* impacted or critical. Handle these device by determining their urgent
+* bkops status at runtime.
+*/
+   if (curr_status < BKOPS_STATUS_PERF_IMPACT) {
+   dev_err(hba->dev, "%s: device raised urgent BKOPS exception for 
bkops status %d\n",
+   __func__, curr_status);
+   /* update the current status as the urgent bkops level */
+   hba->urgent_bkops_lvl = curr_status;
+   hba->is_urgent_bkops_lvl_checked = true;
+   }
+
+enable_auto_bkops:
+   err = ufshcd_enable_auto_bkops(hba);
+out:
+   if (err < 0)
+   dev_err(hba->dev, "%s: failed to handle urgent bkops %d\n",
+   __func__, err);
+}
+
 /**
  * ufshcd_exception_event_handler - handle exceptions raised by device
  * @work: pointer to work data
@@ -3795,12 +3832,10 @@ static void ufshcd_exception_event_handler(struct 
work_struct *work)
}
 
status &= hba->ee_ctrl_mask;
-   if (status & MASK_EE_URGENT_BKOPS) {
-   err = ufshcd_urgent_bkops(hba);
-   if (err < 0)
-   dev_err(hba->dev, "%s: failed to handle urgent bkops 
%d\n",
-   __func__, err);
-   }
+
+   if (status & MASK_EE_URGENT_BKOPS)
+   ufshcd_bkops_exception_event_handler(hba);
+
 out:
pm_runtime_put_sync(hba->dev);
return;
@@ -4832,6 +4867,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
 
ufshcd_init_pwr_info(hba);
 
+   /* set the default level for urgent bkops */
+   hba->urgent_bkops_lvl = BKOPS_STATUS_PERF_IMPACT;
+   hba->is_urgent_bkops_lvl_checked = false;
+
/* UniPro link is active now */
ufshcd_set_link_active(hba);
 
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 77e79c0..65f29aa 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -383,6 +383,9 @@ struct ufs_init_prefetch {
  * @clk_list_head: UFS host controller clocks list node head
  * @pwr_info: holds current power mode
  * @max_pwr_info: keeps the device max valid pwm
+ * @urgent_bkops_lvl: keeps track of urgent bkops level for device
+ * @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for
+ *  device is known or not.
  */
 struct ufs_hba {
void __iomem *mmio_base;
@@ -538,6 +541,9 @@ struct ufs_hba {
struct devfreq *devfreq;
struct ufs_clk_scaling clk_scaling;
bool is_sys_suspended;
+
+   enum bkops_status urgent_bkops_lvl;
+   bool is_urgent_bkops_lvl_checked;
 };
 
 /* Returns true if clocks can be gated. Otherwise false */
-- 
1.8.5.2

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


[PATCH v8 02/17] scsi: ufs: avoid spurious UFS host controller interrupts

2016-03-10 Thread Yaniv Gardi
When control reaches to Linux UFS driver during UFS boot mode, UFS host
controller interrupt status/enable registers may have left over
settings.
In order to avoid any spurious interrupts due to these left overs,
it's important to clear these interrupt status/enable registers before
enabling UFS interrupt handling.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 15 +++
 1 file changed, 15 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a8e42df..de7280c 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5837,6 +5837,21 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem 
*mmio_base, unsigned int irq)
init_waitqueue_head(>dev_cmd.tag_wq);
 
ufshcd_init_clk_gating(hba);
+
+   /*
+* In order to avoid any spurious interrupt immediately after
+* registering UFS controller interrupt handler, clear any pending UFS
+* interrupt status and disable all the UFS interrupts.
+*/
+   ufshcd_writel(hba, ufshcd_readl(hba, REG_INTERRUPT_STATUS),
+ REG_INTERRUPT_STATUS);
+   ufshcd_writel(hba, 0, REG_INTERRUPT_ENABLE);
+   /*
+* Make sure that UFS interrupts are disabled and any pending interrupt
+* status is cleared before registering UFS interrupt handler.
+*/
+   mb();
+
/* IRQ registration */
err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
if (err) {
-- 
1.8.5.2

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


[PATCH v8 11/17] scsi: ufs: handle non spec compliant bkops behaviour by device

2016-03-10 Thread Yaniv Gardi
We are seeing that some devices are raising the urgent bkops exception
events even when BKOPS status doesn't indicate performace impacted or
critical. Handle these device by determining their urgent bkops status
at runtime.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h |  6 ++
 2 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index b429a57..82a4b1c 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3763,7 +3763,7 @@ out:
  */
 static int ufshcd_urgent_bkops(struct ufs_hba *hba)
 {
-   return ufshcd_bkops_ctrl(hba, BKOPS_STATUS_PERF_IMPACT);
+   return ufshcd_bkops_ctrl(hba, hba->urgent_bkops_lvl);
 }
 
 static inline int ufshcd_get_ee_status(struct ufs_hba *hba, u32 *status)
@@ -3772,6 +3772,43 @@ static inline int ufshcd_get_ee_status(struct ufs_hba 
*hba, u32 *status)
QUERY_ATTR_IDN_EE_STATUS, 0, 0, status);
 }
 
+static void ufshcd_bkops_exception_event_handler(struct ufs_hba *hba)
+{
+   int err;
+   u32 curr_status = 0;
+
+   if (hba->is_urgent_bkops_lvl_checked)
+   goto enable_auto_bkops;
+
+   err = ufshcd_get_bkops_status(hba, _status);
+   if (err) {
+   dev_err(hba->dev, "%s: failed to get BKOPS status %d\n",
+   __func__, err);
+   goto out;
+   }
+
+   /*
+* We are seeing that some devices are raising the urgent bkops
+* exception events even when BKOPS status doesn't indicate performace
+* impacted or critical. Handle these device by determining their urgent
+* bkops status at runtime.
+*/
+   if (curr_status < BKOPS_STATUS_PERF_IMPACT) {
+   dev_err(hba->dev, "%s: device raised urgent BKOPS exception for 
bkops status %d\n",
+   __func__, curr_status);
+   /* update the current status as the urgent bkops level */
+   hba->urgent_bkops_lvl = curr_status;
+   hba->is_urgent_bkops_lvl_checked = true;
+   }
+
+enable_auto_bkops:
+   err = ufshcd_enable_auto_bkops(hba);
+out:
+   if (err < 0)
+   dev_err(hba->dev, "%s: failed to handle urgent bkops %d\n",
+   __func__, err);
+}
+
 /**
  * ufshcd_exception_event_handler - handle exceptions raised by device
  * @work: pointer to work data
@@ -3795,12 +3832,10 @@ static void ufshcd_exception_event_handler(struct 
work_struct *work)
}
 
status &= hba->ee_ctrl_mask;
-   if (status & MASK_EE_URGENT_BKOPS) {
-   err = ufshcd_urgent_bkops(hba);
-   if (err < 0)
-   dev_err(hba->dev, "%s: failed to handle urgent bkops 
%d\n",
-   __func__, err);
-   }
+
+   if (status & MASK_EE_URGENT_BKOPS)
+   ufshcd_bkops_exception_event_handler(hba);
+
 out:
pm_runtime_put_sync(hba->dev);
return;
@@ -4832,6 +4867,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
 
ufshcd_init_pwr_info(hba);
 
+   /* set the default level for urgent bkops */
+   hba->urgent_bkops_lvl = BKOPS_STATUS_PERF_IMPACT;
+   hba->is_urgent_bkops_lvl_checked = false;
+
/* UniPro link is active now */
ufshcd_set_link_active(hba);
 
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 77e79c0..65f29aa 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -383,6 +383,9 @@ struct ufs_init_prefetch {
  * @clk_list_head: UFS host controller clocks list node head
  * @pwr_info: holds current power mode
  * @max_pwr_info: keeps the device max valid pwm
+ * @urgent_bkops_lvl: keeps track of urgent bkops level for device
+ * @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for
+ *  device is known or not.
  */
 struct ufs_hba {
void __iomem *mmio_base;
@@ -538,6 +541,9 @@ struct ufs_hba {
struct devfreq *devfreq;
struct ufs_clk_scaling clk_scaling;
bool is_sys_suspended;
+
+   enum bkops_status urgent_bkops_lvl;
+   bool is_urgent_bkops_lvl_checked;
 };
 
 /* Returns true if clocks can be gated. Otherwise false */
-- 
1.8.5.2

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


[PATCH v8 04/17] scsi: ufs: verify hba controller hce reg value

2016-03-10 Thread Yaniv Gardi
Sometimes due to hw issues it takes some time to the
host controller register to update. In order to verify the register
has updated, a polling is done until its value is set.

In addition the functions ufshcd_hba_stop() and
ufshcd_wait_for_register() was updated with an additional input
parameter, indicating the timeout between reads will
be done by sleeping or spinning the cpu.

Reviewed-by: Hannes Reinecke <h...@suse.com>
Signed-off-by: Raviv Shvili <rshv...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h | 12 +++
 2 files changed, 35 insertions(+), 30 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 3400ceb..80031e6 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -240,11 +240,13 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
  * @val - wait condition
  * @interval_us - polling interval in microsecs
  * @timeout_ms - timeout in millisecs
+ * @can_sleep - perform sleep or just spin
  *
  * 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 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
+   u32 val, unsigned long interval_us,
+   unsigned long timeout_ms, bool can_sleep)
 {
int err = 0;
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
@@ -253,9 +255,10 @@ static int ufshcd_wait_for_register(struct ufs_hba *hba, 
u32 reg, u32 mask,
val = val & mask;
 
while ((ufshcd_readl(hba, reg) & mask) != val) {
-   /* wakeup within 50us of expiry */
-   usleep_range(interval_us, interval_us + 50);
-
+   if (can_sleep)
+   usleep_range(interval_us, interval_us + 50);
+   else
+   udelay(interval_us);
if (time_after(jiffies, timeout)) {
if ((ufshcd_readl(hba, reg) & mask) != val)
err = -ETIMEDOUT;
@@ -1459,7 +1462,7 @@ ufshcd_clear_cmd(struct ufs_hba *hba, int tag)
 */
err = ufshcd_wait_for_register(hba,
REG_UTP_TRANSFER_REQ_DOOR_BELL,
-   mask, ~mask, 1000, 1000);
+   mask, ~mask, 1000, 1000, true);
 
return err;
 }
@@ -2815,6 +2818,23 @@ out:
 }
 
 /**
+ * ufshcd_hba_stop - Send controller to reset state
+ * @hba: per adapter instance
+ * @can_sleep: perform sleep or just spin
+ */
+static inline void ufshcd_hba_stop(struct ufs_hba *hba, bool can_sleep)
+{
+   int err;
+
+   ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
+   err = ufshcd_wait_for_register(hba, REG_CONTROLLER_ENABLE,
+   CONTROLLER_ENABLE, CONTROLLER_DISABLE,
+   10, 1, can_sleep);
+   if (err)
+   dev_err(hba->dev, "%s: Controller disable failed\n", __func__);
+}
+
+/**
  * ufshcd_hba_enable - initialize the controller
  * @hba: per adapter instance
  *
@@ -2834,18 +2854,9 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
 * development and testing of this driver. msleep can be changed to
 * mdelay and retry count can be reduced based on the controller.
 */
-   if (!ufshcd_is_hba_active(hba)) {
-
+   if (!ufshcd_is_hba_active(hba))
/* change controller state to "reset state" */
-   ufshcd_hba_stop(hba);
-
-   /*
-* This delay is based on the testing done with UFS host
-* controller FPGA. The delay can be changed based on the
-* host controller used.
-*/
-   msleep(5);
-   }
+   ufshcd_hba_stop(hba, true);
 
/* UniPro link is disabled at this point */
ufshcd_set_link_off(hba);
@@ -3898,7 +3909,7 @@ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int 
tag)
/* 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,
-   mask, 0, 1000, 1000);
+   mask, 0, 1000, 1000, true);
 out:
return err;
 }
@@ -4180,7 +4191,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba 
*hba)
 
/* Reset the host controller */
spin_lock_irqsave(hba->host->host_lock, flags);
-   ufshcd_hba_stop(hba);
+   ufshcd_hba_stop(hba, false);
spin_unlock_irqrestore(hba->host->host_lock, flags);
 
err = ufshcd_hba_enable(hba);
@@ -5133,7 +5144,7 @@ static int ufshcd_link_state_transition(struct uf

[PATCH v8 15/17] scsi: ufs-qcom: set PA_Local_TX_LCC_Enable before link startup

2016-03-10 Thread Yaniv Gardi
Some UFS devices (and may be host) have issues if LCC is
enabled. So we are setting PA_Local_TX_LCC_Enable to 0
before link startup which will make sure that both host
and device TX LCC are disabled once link startup is
completed.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs-qcom.c | 27 ---
 drivers/scsi/ufs/unipro.h   |  1 +
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index ed57729..966bacf 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -16,8 +16,8 @@
 #include 
 #include 
 #include 
-
 #include 
+
 #include "ufshcd.h"
 #include "ufshcd-pltfrm.h"
 #include "unipro.h"
@@ -106,9 +106,11 @@ static void ufs_qcom_disable_lane_clks(struct 
ufs_qcom_host *host)
if (!host->is_lane_clks_enabled)
return;
 
-   clk_disable_unprepare(host->tx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->tx_l1_sync_clk);
clk_disable_unprepare(host->tx_l0_sync_clk);
-   clk_disable_unprepare(host->rx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->rx_l1_sync_clk);
clk_disable_unprepare(host->rx_l0_sync_clk);
 
host->is_lane_clks_enabled = false;
@@ -272,9 +274,8 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B);
 
if (ret) {
-   dev_err(hba->dev,
-   "%s: ufs_qcom_phy_calibrate_phy()failed, ret = %d\n",
-   __func__, ret);
+   dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret 
= %d\n",
+   __func__, ret);
goto out;
}
 
@@ -524,6 +525,18 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba 
*hba,
err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba,
  150);
 
+   /*
+* Some UFS devices (and may be host) have issues if LCC is
+* enabled. So we are setting PA_Local_TX_LCC_Enable to 0
+* before link startup which will make sure that both host
+* and device TX LCC are disabled once link startup is
+* completed.
+*/
+   if (ufshcd_get_local_unipro_ver(hba) != UFS_UNIPRO_VER_1_41)
+   err = ufshcd_dme_set(hba,
+   UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE),
+   0);
+
break;
case POST_CHANGE:
ufs_qcom_link_startup_post_change(hba);
@@ -1542,7 +1555,7 @@ static int ufs_qcom_probe(struct platform_device *pdev)
  * ufs_qcom_remove - set driver_data of the device to NULL
  * @pdev: pointer to platform device handle
  *
- * Always return 0
+ * Always returns 0
  */
 static int ufs_qcom_remove(struct platform_device *pdev)
 {
diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h
index 8082020..e2854e4 100644
--- a/drivers/scsi/ufs/unipro.h
+++ b/drivers/scsi/ufs/unipro.h
@@ -75,6 +75,7 @@
 #define PA_MAXRXSPEEDFAST  0x1541
 #define PA_MAXRXSPEEDSLOW  0x1542
 #define PA_TXLINKSTARTUPHS 0x1544
+#define PA_LOCAL_TX_LCC_ENABLE 0x155E
 #define PA_TXSPEEDFAST 0x1565
 #define PA_TXSPEEDSLOW 0x1566
 #define PA_REMOTEVERINFO   0x15A0
-- 
1.8.5.2

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


[PATCH v8 04/17] scsi: ufs: verify hba controller hce reg value

2016-03-10 Thread Yaniv Gardi
Sometimes due to hw issues it takes some time to the
host controller register to update. In order to verify the register
has updated, a polling is done until its value is set.

In addition the functions ufshcd_hba_stop() and
ufshcd_wait_for_register() was updated with an additional input
parameter, indicating the timeout between reads will
be done by sleeping or spinning the cpu.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Raviv Shvili 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h | 12 +++
 2 files changed, 35 insertions(+), 30 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 3400ceb..80031e6 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -240,11 +240,13 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
  * @val - wait condition
  * @interval_us - polling interval in microsecs
  * @timeout_ms - timeout in millisecs
+ * @can_sleep - perform sleep or just spin
  *
  * 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 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
+   u32 val, unsigned long interval_us,
+   unsigned long timeout_ms, bool can_sleep)
 {
int err = 0;
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
@@ -253,9 +255,10 @@ static int ufshcd_wait_for_register(struct ufs_hba *hba, 
u32 reg, u32 mask,
val = val & mask;
 
while ((ufshcd_readl(hba, reg) & mask) != val) {
-   /* wakeup within 50us of expiry */
-   usleep_range(interval_us, interval_us + 50);
-
+   if (can_sleep)
+   usleep_range(interval_us, interval_us + 50);
+   else
+   udelay(interval_us);
if (time_after(jiffies, timeout)) {
if ((ufshcd_readl(hba, reg) & mask) != val)
err = -ETIMEDOUT;
@@ -1459,7 +1462,7 @@ ufshcd_clear_cmd(struct ufs_hba *hba, int tag)
 */
err = ufshcd_wait_for_register(hba,
REG_UTP_TRANSFER_REQ_DOOR_BELL,
-   mask, ~mask, 1000, 1000);
+   mask, ~mask, 1000, 1000, true);
 
return err;
 }
@@ -2815,6 +2818,23 @@ out:
 }
 
 /**
+ * ufshcd_hba_stop - Send controller to reset state
+ * @hba: per adapter instance
+ * @can_sleep: perform sleep or just spin
+ */
+static inline void ufshcd_hba_stop(struct ufs_hba *hba, bool can_sleep)
+{
+   int err;
+
+   ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
+   err = ufshcd_wait_for_register(hba, REG_CONTROLLER_ENABLE,
+   CONTROLLER_ENABLE, CONTROLLER_DISABLE,
+   10, 1, can_sleep);
+   if (err)
+   dev_err(hba->dev, "%s: Controller disable failed\n", __func__);
+}
+
+/**
  * ufshcd_hba_enable - initialize the controller
  * @hba: per adapter instance
  *
@@ -2834,18 +2854,9 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
 * development and testing of this driver. msleep can be changed to
 * mdelay and retry count can be reduced based on the controller.
 */
-   if (!ufshcd_is_hba_active(hba)) {
-
+   if (!ufshcd_is_hba_active(hba))
/* change controller state to "reset state" */
-   ufshcd_hba_stop(hba);
-
-   /*
-* This delay is based on the testing done with UFS host
-* controller FPGA. The delay can be changed based on the
-* host controller used.
-*/
-   msleep(5);
-   }
+   ufshcd_hba_stop(hba, true);
 
/* UniPro link is disabled at this point */
ufshcd_set_link_off(hba);
@@ -3898,7 +3909,7 @@ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int 
tag)
/* 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,
-   mask, 0, 1000, 1000);
+   mask, 0, 1000, 1000, true);
 out:
return err;
 }
@@ -4180,7 +4191,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba 
*hba)
 
/* Reset the host controller */
spin_lock_irqsave(hba->host->host_lock, flags);
-   ufshcd_hba_stop(hba);
+   ufshcd_hba_stop(hba, false);
spin_unlock_irqrestore(hba->host->host_lock, flags);
 
err = ufshcd_hba_enable(hba);
@@ -5133,7 +5144,7 @@ static int ufshcd_link_state_transition(struct ufs_hba 
*hba,
 * Change controller state to "reset state"

[PATCH v8 15/17] scsi: ufs-qcom: set PA_Local_TX_LCC_Enable before link startup

2016-03-10 Thread Yaniv Gardi
Some UFS devices (and may be host) have issues if LCC is
enabled. So we are setting PA_Local_TX_LCC_Enable to 0
before link startup which will make sure that both host
and device TX LCC are disabled once link startup is
completed.

Reviewed-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs-qcom.c | 27 ---
 drivers/scsi/ufs/unipro.h   |  1 +
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index ed57729..966bacf 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -16,8 +16,8 @@
 #include 
 #include 
 #include 
-
 #include 
+
 #include "ufshcd.h"
 #include "ufshcd-pltfrm.h"
 #include "unipro.h"
@@ -106,9 +106,11 @@ static void ufs_qcom_disable_lane_clks(struct 
ufs_qcom_host *host)
if (!host->is_lane_clks_enabled)
return;
 
-   clk_disable_unprepare(host->tx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->tx_l1_sync_clk);
clk_disable_unprepare(host->tx_l0_sync_clk);
-   clk_disable_unprepare(host->rx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->rx_l1_sync_clk);
clk_disable_unprepare(host->rx_l0_sync_clk);
 
host->is_lane_clks_enabled = false;
@@ -272,9 +274,8 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B);
 
if (ret) {
-   dev_err(hba->dev,
-   "%s: ufs_qcom_phy_calibrate_phy()failed, ret = %d\n",
-   __func__, ret);
+   dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret 
= %d\n",
+   __func__, ret);
goto out;
}
 
@@ -524,6 +525,18 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba 
*hba,
err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba,
  150);
 
+   /*
+* Some UFS devices (and may be host) have issues if LCC is
+* enabled. So we are setting PA_Local_TX_LCC_Enable to 0
+* before link startup which will make sure that both host
+* and device TX LCC are disabled once link startup is
+* completed.
+*/
+   if (ufshcd_get_local_unipro_ver(hba) != UFS_UNIPRO_VER_1_41)
+   err = ufshcd_dme_set(hba,
+   UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE),
+   0);
+
break;
case POST_CHANGE:
ufs_qcom_link_startup_post_change(hba);
@@ -1542,7 +1555,7 @@ static int ufs_qcom_probe(struct platform_device *pdev)
  * ufs_qcom_remove - set driver_data of the device to NULL
  * @pdev: pointer to platform device handle
  *
- * Always return 0
+ * Always returns 0
  */
 static int ufs_qcom_remove(struct platform_device *pdev)
 {
diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h
index 8082020..e2854e4 100644
--- a/drivers/scsi/ufs/unipro.h
+++ b/drivers/scsi/ufs/unipro.h
@@ -75,6 +75,7 @@
 #define PA_MAXRXSPEEDFAST  0x1541
 #define PA_MAXRXSPEEDSLOW  0x1542
 #define PA_TXLINKSTARTUPHS 0x1544
+#define PA_LOCAL_TX_LCC_ENABLE 0x155E
 #define PA_TXSPEEDFAST 0x1565
 #define PA_TXSPEEDSLOW 0x1566
 #define PA_REMOTEVERINFO   0x15A0
-- 
1.8.5.2

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


[PATCH v8 00/17] add fixes, device quirks, error recovery,

2016-03-10 Thread Yaniv Gardi
V8:
fixed cyclic dependency by removing ufs_quirk.c that was previously added
in V7 06/17, and moving its code into ufshcd.c

V7:
updated patches with "Reviewed-by" and "Acked-by" tags

V6:
as per comment, patch 15/15 in V5 was divided into 3 patches in V6

V5:
as per comment, patch 05/14 in V4, was divided into 2 patches in V5

V4:
fixed DOS EOL symbols in 2 new files

V3:
Due to comments in V2
Removed change 02/17 of V2 
Removed change 03/17 of V2
Removed change 17/17 of V2

V2:
This series should be pushed on top of 15 patches series:
"Big fixes, retries, handle a race condition"
fixed and few comments.

V1:
This series should be pushed on top of 15 patches series:
"Big fixes, retries, handle a race condition"


Yaniv Gardi (15):
  scsi: ufs-qcom: add number of lanes per direction
  scsi: ufs: avoid spurious UFS host controller interrupts
  scsi: ufs: implement scsi host timeout handler
  scsi: ufs: verify hba controller hce reg value
  scsi: ufs: add support to read device and string descriptors
  scsi: ufs: separate device and host quirks
  scsi: ufs: disable vccq if it's not needed by UFS device
  scsi: ufs: make error handling bit faster
  scsi: ufs: add error recovery after DL NAC error
  scsi: ufs: add retry for query descriptors
  scsi: ufs: handle non spec compliant bkops behaviour by device
  scsi: ufs: tune UniPro parameters to optimize hibern8 exit time
  scsi: ufs: fix leakage during link off state
  scsi: ufs: add device quirk delay before putting UFS rails in LPM
  scsi: ufs-qcom: set PA_Local_TX_LCC_Enable before link startup
  scsi: ufs-qcom: enable/disable the device ref clock
  scsi: ufs-qcom: add printouts of testbus debug registers

 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |   3 +
 drivers/scsi/ufs/ufs-qcom.c| 155 +++-
 drivers/scsi/ufs/ufs-qcom.h|   9 +
 drivers/scsi/ufs/ufs.h |  33 +
 drivers/scsi/ufs/ufs_quirks.h  | 151 
 drivers/scsi/ufs/ufshcd-pltfrm.c   |  19 +
 drivers/scsi/ufs/ufshcd.c  | 818 ++---
 drivers/scsi/ufs/ufshcd.h  |  40 +-
 drivers/scsi/ufs/ufshci.h  |   4 +
 drivers/scsi/ufs/unipro.h  |  22 +
 10 files changed, 1127 insertions(+), 127 deletions(-)
 create mode 100644 drivers/scsi/ufs/ufs_quirks.h

-- 
1.8.5.2

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


[PATCH v8 00/17] add fixes, device quirks, error recovery,

2016-03-10 Thread Yaniv Gardi
V8:
fixed cyclic dependency by removing ufs_quirk.c that was previously added
in V7 06/17, and moving its code into ufshcd.c

V7:
updated patches with "Reviewed-by" and "Acked-by" tags

V6:
as per comment, patch 15/15 in V5 was divided into 3 patches in V6

V5:
as per comment, patch 05/14 in V4, was divided into 2 patches in V5

V4:
fixed DOS EOL symbols in 2 new files

V3:
Due to comments in V2
Removed change 02/17 of V2 
Removed change 03/17 of V2
Removed change 17/17 of V2

V2:
This series should be pushed on top of 15 patches series:
"Big fixes, retries, handle a race condition"
fixed and few comments.

V1:
This series should be pushed on top of 15 patches series:
"Big fixes, retries, handle a race condition"


Yaniv Gardi (15):
  scsi: ufs-qcom: add number of lanes per direction
  scsi: ufs: avoid spurious UFS host controller interrupts
  scsi: ufs: implement scsi host timeout handler
  scsi: ufs: verify hba controller hce reg value
  scsi: ufs: add support to read device and string descriptors
  scsi: ufs: separate device and host quirks
  scsi: ufs: disable vccq if it's not needed by UFS device
  scsi: ufs: make error handling bit faster
  scsi: ufs: add error recovery after DL NAC error
  scsi: ufs: add retry for query descriptors
  scsi: ufs: handle non spec compliant bkops behaviour by device
  scsi: ufs: tune UniPro parameters to optimize hibern8 exit time
  scsi: ufs: fix leakage during link off state
  scsi: ufs: add device quirk delay before putting UFS rails in LPM
  scsi: ufs-qcom: set PA_Local_TX_LCC_Enable before link startup
  scsi: ufs-qcom: enable/disable the device ref clock
  scsi: ufs-qcom: add printouts of testbus debug registers

 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |   3 +
 drivers/scsi/ufs/ufs-qcom.c| 155 +++-
 drivers/scsi/ufs/ufs-qcom.h|   9 +
 drivers/scsi/ufs/ufs.h |  33 +
 drivers/scsi/ufs/ufs_quirks.h  | 151 
 drivers/scsi/ufs/ufshcd-pltfrm.c   |  19 +
 drivers/scsi/ufs/ufshcd.c  | 818 ++---
 drivers/scsi/ufs/ufshcd.h  |  40 +-
 drivers/scsi/ufs/ufshci.h  |   4 +
 drivers/scsi/ufs/unipro.h  |  22 +
 10 files changed, 1127 insertions(+), 127 deletions(-)
 create mode 100644 drivers/scsi/ufs/ufs_quirks.h

-- 
1.8.5.2

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


[PATCH v8 13/17] scsi: ufs: fix leakage during link off state

2016-03-10 Thread Yaniv Gardi
Currently when we try to put the link in off/disabled state during
suspend, it seems link is not being kept in low power mode.
This patch fixes the issue by putting the link in hibern8 first
(so device also puts the link in low power mode) and then stop the
host controller.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 305210f..93cc02f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5688,6 +5688,16 @@ static int ufshcd_link_state_transition(struct ufs_hba 
*hba,
   (!check_for_bkops || (check_for_bkops &&
!hba->auto_bkops_enabled))) {
/*
+* Let's make sure that link is in low power mode, we are doing
+* this currently by putting the link in Hibern8. Otherway to
+* put the link in low power mode is to send the DME end point
+* to device and then send the DME reset command to local
+* unipro. But putting the link in hibern8 is much faster.
+*/
+   ret = ufshcd_uic_hibern8_enter(hba);
+   if (ret)
+   goto out;
+   /*
 * Change controller state to "reset state" which
 * should also put the link in off/reset state
 */
-- 
1.8.5.2

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


[PATCH v8 01/17] scsi: ufs-qcom: add number of lanes per direction

2016-03-10 Thread Yaniv Gardi
Different platform may have different number of lanes
for the UFS link.
Add parameter to device tree specifying how many lanes
should be configured for the UFS link.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Acked-by: Rob Herring <r...@kernel.org>
Signed-off-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |  3 ++
 drivers/scsi/ufs/ufs-qcom.c| 39 --
 drivers/scsi/ufs/ufshcd-pltfrm.c   | 19 +++
 drivers/scsi/ufs/ufshcd.c  |  1 +
 drivers/scsi/ufs/ufshcd.h  |  2 ++
 5 files changed, 47 insertions(+), 17 deletions(-)

diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt 
b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 03c0e98..66f6adf 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -38,6 +38,9 @@ Optional properties:
  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.
+-lanes-per-direction   : number of lanes available per direction - either 1 or 
2.
+ Note that it is assume same number of lanes is used 
both
+ directions at once. If not specified, default is 2 
lanes per direction.
 
 Note: If above properties are not defined it can be assumed that the supply
 regulators or clocks are always on.
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 4f38d00..ed57729 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016, Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -132,21 +132,24 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host 
*host)
if (err)
goto disable_rx_l0;
 
-   err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk",
-   host->rx_l1_sync_clk);
-   if (err)
-   goto disable_tx_l0;
+   if (host->hba->lanes_per_direction > 1) {
+   err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk",
+   host->rx_l1_sync_clk);
+   if (err)
+   goto disable_tx_l0;
 
-   err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk",
-   host->tx_l1_sync_clk);
-   if (err)
-   goto disable_rx_l1;
+   err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk",
+   host->tx_l1_sync_clk);
+   if (err)
+   goto disable_rx_l1;
+   }
 
host->is_lane_clks_enabled = true;
goto out;
 
 disable_rx_l1:
-   clk_disable_unprepare(host->rx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->rx_l1_sync_clk);
 disable_tx_l0:
clk_disable_unprepare(host->tx_l0_sync_clk);
 disable_rx_l0:
@@ -170,14 +173,16 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host 
*host)
if (err)
goto out;
 
-   err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
-   >rx_l1_sync_clk);
-   if (err)
-   goto out;
-
-   err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
-   >tx_l1_sync_clk);
+   /* In case of single lane per direction, don't read lane1 clocks */
+   if (host->hba->lanes_per_direction > 1) {
+   err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
+   >rx_l1_sync_clk);
+   if (err)
+   goto out;
 
+   err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
+   >tx_l1_sync_clk);
+   }
 out:
return err;
 }
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index d2a7b12..718f12e 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -40,6 +40,8 @@
 #include "ufshcd.h"
 #include "ufshcd-pltfrm.h"
 
+#define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2
+
 static int ufshcd_parse_clock_info(struct ufs_hba *hba)
 {
int ret = 0;
@@ -277,6 +279,21 @@ void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
 }
 EXPORT_SYMBOL_GPL(ufshcd_pltfrm_shutdown);
 
+static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
+{
+   struct device *

[PATCH v8 13/17] scsi: ufs: fix leakage during link off state

2016-03-10 Thread Yaniv Gardi
Currently when we try to put the link in off/disabled state during
suspend, it seems link is not being kept in low power mode.
This patch fixes the issue by putting the link in hibern8 first
(so device also puts the link in low power mode) and then stop the
host controller.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 305210f..93cc02f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5688,6 +5688,16 @@ static int ufshcd_link_state_transition(struct ufs_hba 
*hba,
   (!check_for_bkops || (check_for_bkops &&
!hba->auto_bkops_enabled))) {
/*
+* Let's make sure that link is in low power mode, we are doing
+* this currently by putting the link in Hibern8. Otherway to
+* put the link in low power mode is to send the DME end point
+* to device and then send the DME reset command to local
+* unipro. But putting the link in hibern8 is much faster.
+*/
+   ret = ufshcd_uic_hibern8_enter(hba);
+   if (ret)
+   goto out;
+   /*
 * Change controller state to "reset state" which
 * should also put the link in off/reset state
 */
-- 
1.8.5.2

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


[PATCH v8 01/17] scsi: ufs-qcom: add number of lanes per direction

2016-03-10 Thread Yaniv Gardi
Different platform may have different number of lanes
for the UFS link.
Add parameter to device tree specifying how many lanes
should be configured for the UFS link.

Reviewed-by: Hannes Reinecke 
Acked-by: Rob Herring 
Signed-off-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |  3 ++
 drivers/scsi/ufs/ufs-qcom.c| 39 --
 drivers/scsi/ufs/ufshcd-pltfrm.c   | 19 +++
 drivers/scsi/ufs/ufshcd.c  |  1 +
 drivers/scsi/ufs/ufshcd.h  |  2 ++
 5 files changed, 47 insertions(+), 17 deletions(-)

diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt 
b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 03c0e98..66f6adf 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -38,6 +38,9 @@ Optional properties:
  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.
+-lanes-per-direction   : number of lanes available per direction - either 1 or 
2.
+ Note that it is assume same number of lanes is used 
both
+ directions at once. If not specified, default is 2 
lanes per direction.
 
 Note: If above properties are not defined it can be assumed that the supply
 regulators or clocks are always on.
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 4f38d00..ed57729 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016, Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -132,21 +132,24 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host 
*host)
if (err)
goto disable_rx_l0;
 
-   err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk",
-   host->rx_l1_sync_clk);
-   if (err)
-   goto disable_tx_l0;
+   if (host->hba->lanes_per_direction > 1) {
+   err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk",
+   host->rx_l1_sync_clk);
+   if (err)
+   goto disable_tx_l0;
 
-   err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk",
-   host->tx_l1_sync_clk);
-   if (err)
-   goto disable_rx_l1;
+   err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk",
+   host->tx_l1_sync_clk);
+   if (err)
+   goto disable_rx_l1;
+   }
 
host->is_lane_clks_enabled = true;
goto out;
 
 disable_rx_l1:
-   clk_disable_unprepare(host->rx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->rx_l1_sync_clk);
 disable_tx_l0:
clk_disable_unprepare(host->tx_l0_sync_clk);
 disable_rx_l0:
@@ -170,14 +173,16 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host 
*host)
if (err)
goto out;
 
-   err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
-   >rx_l1_sync_clk);
-   if (err)
-   goto out;
-
-   err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
-   >tx_l1_sync_clk);
+   /* In case of single lane per direction, don't read lane1 clocks */
+   if (host->hba->lanes_per_direction > 1) {
+   err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
+   >rx_l1_sync_clk);
+   if (err)
+   goto out;
 
+   err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
+   >tx_l1_sync_clk);
+   }
 out:
return err;
 }
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index d2a7b12..718f12e 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -40,6 +40,8 @@
 #include "ufshcd.h"
 #include "ufshcd-pltfrm.h"
 
+#define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2
+
 static int ufshcd_parse_clock_info(struct ufs_hba *hba)
 {
int ret = 0;
@@ -277,6 +279,21 @@ void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
 }
 EXPORT_SYMBOL_GPL(ufshcd_pltfrm_shutdown);
 
+static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
+{
+   struct device *dev = hba->dev;
+   int ret;
+
+   ret = of_property_read_u32(dev->of_node, "lan

[PATCH v7] scsi: ufs: add ioctl interface for query request

2016-03-09 Thread Yaniv Gardi
This patch exposes the ioctl interface for UFS driver via SCSI device
ioctl interface. As of now UFS driver would provide the ioctl for query
interface to connected UFS device.

Reviewed-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Dolev Raviv <dra...@codeaurora.org>
Signed-off-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs.h|  53 +++
 drivers/scsi/ufs/ufshcd.c | 208 +-
 include/uapi/scsi/Kbuild  |   1 +
 include/uapi/scsi/ufs/Kbuild  |   3 +
 include/uapi/scsi/ufs/ioctl.h |  58 
 include/uapi/scsi/ufs/ufs.h   |  67 ++
 6 files changed, 347 insertions(+), 43 deletions(-)
 create mode 100644 include/uapi/scsi/ufs/Kbuild
 create mode 100644 include/uapi/scsi/ufs/ioctl.h
 create mode 100644 include/uapi/scsi/ufs/ufs.h

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index b291fa6..d39410f 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -38,6 +38,7 @@
 
 #include 
 #include 
+#include 
 
 #define MAX_CDB_SIZE   16
 #define GENERAL_UPIU_REQUEST_SIZE 32
@@ -72,6 +73,16 @@ enum {
UFS_UPIU_RPMB_WLUN  = 0xC4,
 };
 
+/**
+ * ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor
+ * @lun: LU number to check
+ * @return: true if the lun has a matching unit descriptor, false otherwise
+ */
+static inline bool ufs_is_valid_unit_desc_lun(u8 lun)
+{
+   return lun == UFS_UPIU_RPMB_WLUN || (lun < UFS_UPIU_MAX_GENERAL_LUN);
+}
+
 /*
  * UFS Protocol Information Unit related definitions
  */
@@ -127,35 +138,6 @@ enum {
UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST  = 0x81,
 };
 
-/* Flag idn for Query Requests*/
-enum flag_idn {
-   QUERY_FLAG_IDN_FDEVICEINIT  = 0x01,
-   QUERY_FLAG_IDN_PWR_ON_WPE   = 0x03,
-   QUERY_FLAG_IDN_BKOPS_EN = 0x04,
-};
-
-/* Attribute idn for Query requests */
-enum attr_idn {
-   QUERY_ATTR_IDN_ACTIVE_ICC_LVL   = 0x03,
-   QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
-   QUERY_ATTR_IDN_EE_CONTROL   = 0x0D,
-   QUERY_ATTR_IDN_EE_STATUS= 0x0E,
-};
-
-/* Descriptor idn for Query requests */
-enum desc_idn {
-   QUERY_DESC_IDN_DEVICE   = 0x0,
-   QUERY_DESC_IDN_CONFIGURAION = 0x1,
-   QUERY_DESC_IDN_UNIT = 0x2,
-   QUERY_DESC_IDN_RFU_0= 0x3,
-   QUERY_DESC_IDN_INTERCONNECT = 0x4,
-   QUERY_DESC_IDN_STRING   = 0x5,
-   QUERY_DESC_IDN_RFU_1= 0x6,
-   QUERY_DESC_IDN_GEOMETRY = 0x7,
-   QUERY_DESC_IDN_POWER= 0x8,
-   QUERY_DESC_IDN_MAX,
-};
-
 enum desc_header_offset {
QUERY_DESC_LENGTH_OFFSET= 0x00,
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
@@ -279,19 +261,6 @@ enum bkops_status {
BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL,
 };
 
-/* UTP QUERY Transaction Specific Fields OpCode */
-enum query_opcode {
-   UPIU_QUERY_OPCODE_NOP   = 0x0,
-   UPIU_QUERY_OPCODE_READ_DESC = 0x1,
-   UPIU_QUERY_OPCODE_WRITE_DESC= 0x2,
-   UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
-   UPIU_QUERY_OPCODE_WRITE_ATTR= 0x4,
-   UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
-   UPIU_QUERY_OPCODE_SET_FLAG  = 0x6,
-   UPIU_QUERY_OPCODE_CLEAR_FLAG= 0x7,
-   UPIU_QUERY_OPCODE_TOGGLE_FLAG   = 0x8,
-};
-
 /* Query response result code */
 enum {
QUERY_RESULT_SUCCESS= 0x00,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index da882cf..65a9d39 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -38,6 +38,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -2178,7 +2179,7 @@ static inline int ufshcd_read_unit_desc_param(struct 
ufs_hba *hba,
 * Unit descriptors are only available for general purpose LUs (LUN id
 * from 0 to 7) and RPMB Well known LU.
 */
-   if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN))
+   if (!ufs_is_valid_unit_desc_lun(lun))
return -EOPNOTSUPP;
 
return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun,
@@ -5001,6 +5002,207 @@ out:
 }
 
 /**
+ * ufshcd_query_ioctl - perform user read queries
+ * @hba: per-adapter instance
+ * @lun: used for lun specific queries
+ * @buffer: user space buffer for reading and submitting query data and params
+ * @return: 0 for success negative error code otherwise
+ *
+ * Expected/Submitted buffer structure is struct ufs_ioctl_query_data.
+ * It will read the opcode, idn and buf_length parameters, and, put the
+ * response in the buffer field while updating the used size in buf_length.
+ */
+static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer)
+{
+   struct ufs_ioctl_query_data ioctl_data;
+   int err = 0;
+   int length = 0

[PATCH v7] scsi: ufs: add ioctl interface for query request

2016-03-09 Thread Yaniv Gardi
This patch exposes the ioctl interface for UFS driver via SCSI device
ioctl interface. As of now UFS driver would provide the ioctl for query
interface to connected UFS device.

Reviewed-by: Subhash Jadavani 
Signed-off-by: Dolev Raviv 
Signed-off-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs.h|  53 +++
 drivers/scsi/ufs/ufshcd.c | 208 +-
 include/uapi/scsi/Kbuild  |   1 +
 include/uapi/scsi/ufs/Kbuild  |   3 +
 include/uapi/scsi/ufs/ioctl.h |  58 
 include/uapi/scsi/ufs/ufs.h   |  67 ++
 6 files changed, 347 insertions(+), 43 deletions(-)
 create mode 100644 include/uapi/scsi/ufs/Kbuild
 create mode 100644 include/uapi/scsi/ufs/ioctl.h
 create mode 100644 include/uapi/scsi/ufs/ufs.h

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index b291fa6..d39410f 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -38,6 +38,7 @@
 
 #include 
 #include 
+#include 
 
 #define MAX_CDB_SIZE   16
 #define GENERAL_UPIU_REQUEST_SIZE 32
@@ -72,6 +73,16 @@ enum {
UFS_UPIU_RPMB_WLUN  = 0xC4,
 };
 
+/**
+ * ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor
+ * @lun: LU number to check
+ * @return: true if the lun has a matching unit descriptor, false otherwise
+ */
+static inline bool ufs_is_valid_unit_desc_lun(u8 lun)
+{
+   return lun == UFS_UPIU_RPMB_WLUN || (lun < UFS_UPIU_MAX_GENERAL_LUN);
+}
+
 /*
  * UFS Protocol Information Unit related definitions
  */
@@ -127,35 +138,6 @@ enum {
UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST  = 0x81,
 };
 
-/* Flag idn for Query Requests*/
-enum flag_idn {
-   QUERY_FLAG_IDN_FDEVICEINIT  = 0x01,
-   QUERY_FLAG_IDN_PWR_ON_WPE   = 0x03,
-   QUERY_FLAG_IDN_BKOPS_EN = 0x04,
-};
-
-/* Attribute idn for Query requests */
-enum attr_idn {
-   QUERY_ATTR_IDN_ACTIVE_ICC_LVL   = 0x03,
-   QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
-   QUERY_ATTR_IDN_EE_CONTROL   = 0x0D,
-   QUERY_ATTR_IDN_EE_STATUS= 0x0E,
-};
-
-/* Descriptor idn for Query requests */
-enum desc_idn {
-   QUERY_DESC_IDN_DEVICE   = 0x0,
-   QUERY_DESC_IDN_CONFIGURAION = 0x1,
-   QUERY_DESC_IDN_UNIT = 0x2,
-   QUERY_DESC_IDN_RFU_0= 0x3,
-   QUERY_DESC_IDN_INTERCONNECT = 0x4,
-   QUERY_DESC_IDN_STRING   = 0x5,
-   QUERY_DESC_IDN_RFU_1= 0x6,
-   QUERY_DESC_IDN_GEOMETRY = 0x7,
-   QUERY_DESC_IDN_POWER= 0x8,
-   QUERY_DESC_IDN_MAX,
-};
-
 enum desc_header_offset {
QUERY_DESC_LENGTH_OFFSET= 0x00,
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
@@ -279,19 +261,6 @@ enum bkops_status {
BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL,
 };
 
-/* UTP QUERY Transaction Specific Fields OpCode */
-enum query_opcode {
-   UPIU_QUERY_OPCODE_NOP   = 0x0,
-   UPIU_QUERY_OPCODE_READ_DESC = 0x1,
-   UPIU_QUERY_OPCODE_WRITE_DESC= 0x2,
-   UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
-   UPIU_QUERY_OPCODE_WRITE_ATTR= 0x4,
-   UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
-   UPIU_QUERY_OPCODE_SET_FLAG  = 0x6,
-   UPIU_QUERY_OPCODE_CLEAR_FLAG= 0x7,
-   UPIU_QUERY_OPCODE_TOGGLE_FLAG   = 0x8,
-};
-
 /* Query response result code */
 enum {
QUERY_RESULT_SUCCESS= 0x00,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index da882cf..65a9d39 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -38,6 +38,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -2178,7 +2179,7 @@ static inline int ufshcd_read_unit_desc_param(struct 
ufs_hba *hba,
 * Unit descriptors are only available for general purpose LUs (LUN id
 * from 0 to 7) and RPMB Well known LU.
 */
-   if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN))
+   if (!ufs_is_valid_unit_desc_lun(lun))
return -EOPNOTSUPP;
 
return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun,
@@ -5001,6 +5002,207 @@ out:
 }
 
 /**
+ * ufshcd_query_ioctl - perform user read queries
+ * @hba: per-adapter instance
+ * @lun: used for lun specific queries
+ * @buffer: user space buffer for reading and submitting query data and params
+ * @return: 0 for success negative error code otherwise
+ *
+ * Expected/Submitted buffer structure is struct ufs_ioctl_query_data.
+ * It will read the opcode, idn and buf_length parameters, and, put the
+ * response in the buffer field while updating the used size in buf_length.
+ */
+static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer)
+{
+   struct ufs_ioctl_query_data ioctl_data;
+   int err = 0;
+   int length = 0;
+   void *data_ptr;
+   bool flag;
+   u32 att;
+   u8 index;
+   u8 *desc = NULL;
+
+   /* 

[PATCH v6] scsi: ufs: add ioctl interface for query request

2016-03-09 Thread Yaniv Gardi
This patch exposes the ioctl interface for UFS driver via SCSI device
ioctl interface. As of now UFS driver would provide the ioctl for query
interface to connected UFS device.

Reviewed-by: Arnd Bergmann <a...@arndb.de>
Reviewed-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Dolev Raviv <dra...@codeaurora.org>
Signed-off-by: Noa Rubens <n...@codeaurora.org>
Signed-off-by: Raviv Shvili <rshv...@codeaurora.org>
Signed-off-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>
---
 drivers/scsi/ufs/ufs.h|  53 +++
 drivers/scsi/ufs/ufshcd.c | 208 +-
 include/uapi/scsi/Kbuild  |   1 +
 include/uapi/scsi/ufs/Kbuild  |   3 +
 include/uapi/scsi/ufs/ioctl.h |  58 
 include/uapi/scsi/ufs/ufs.h   |  67 ++
 6 files changed, 347 insertions(+), 43 deletions(-)
 create mode 100644 include/uapi/scsi/ufs/Kbuild
 create mode 100644 include/uapi/scsi/ufs/ioctl.h
 create mode 100644 include/uapi/scsi/ufs/ufs.h

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index b291fa6..d39410f 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -38,6 +38,7 @@
 
 #include 
 #include 
+#include 
 
 #define MAX_CDB_SIZE   16
 #define GENERAL_UPIU_REQUEST_SIZE 32
@@ -72,6 +73,16 @@ enum {
UFS_UPIU_RPMB_WLUN  = 0xC4,
 };
 
+/**
+ * ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor
+ * @lun: LU number to check
+ * @return: true if the lun has a matching unit descriptor, false otherwise
+ */
+static inline bool ufs_is_valid_unit_desc_lun(u8 lun)
+{
+   return lun == UFS_UPIU_RPMB_WLUN || (lun < UFS_UPIU_MAX_GENERAL_LUN);
+}
+
 /*
  * UFS Protocol Information Unit related definitions
  */
@@ -127,35 +138,6 @@ enum {
UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST  = 0x81,
 };
 
-/* Flag idn for Query Requests*/
-enum flag_idn {
-   QUERY_FLAG_IDN_FDEVICEINIT  = 0x01,
-   QUERY_FLAG_IDN_PWR_ON_WPE   = 0x03,
-   QUERY_FLAG_IDN_BKOPS_EN = 0x04,
-};
-
-/* Attribute idn for Query requests */
-enum attr_idn {
-   QUERY_ATTR_IDN_ACTIVE_ICC_LVL   = 0x03,
-   QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
-   QUERY_ATTR_IDN_EE_CONTROL   = 0x0D,
-   QUERY_ATTR_IDN_EE_STATUS= 0x0E,
-};
-
-/* Descriptor idn for Query requests */
-enum desc_idn {
-   QUERY_DESC_IDN_DEVICE   = 0x0,
-   QUERY_DESC_IDN_CONFIGURAION = 0x1,
-   QUERY_DESC_IDN_UNIT = 0x2,
-   QUERY_DESC_IDN_RFU_0= 0x3,
-   QUERY_DESC_IDN_INTERCONNECT = 0x4,
-   QUERY_DESC_IDN_STRING   = 0x5,
-   QUERY_DESC_IDN_RFU_1= 0x6,
-   QUERY_DESC_IDN_GEOMETRY = 0x7,
-   QUERY_DESC_IDN_POWER= 0x8,
-   QUERY_DESC_IDN_MAX,
-};
-
 enum desc_header_offset {
QUERY_DESC_LENGTH_OFFSET= 0x00,
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
@@ -279,19 +261,6 @@ enum bkops_status {
BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL,
 };
 
-/* UTP QUERY Transaction Specific Fields OpCode */
-enum query_opcode {
-   UPIU_QUERY_OPCODE_NOP   = 0x0,
-   UPIU_QUERY_OPCODE_READ_DESC = 0x1,
-   UPIU_QUERY_OPCODE_WRITE_DESC= 0x2,
-   UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
-   UPIU_QUERY_OPCODE_WRITE_ATTR= 0x4,
-   UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
-   UPIU_QUERY_OPCODE_SET_FLAG  = 0x6,
-   UPIU_QUERY_OPCODE_CLEAR_FLAG= 0x7,
-   UPIU_QUERY_OPCODE_TOGGLE_FLAG   = 0x8,
-};
-
 /* Query response result code */
 enum {
QUERY_RESULT_SUCCESS= 0x00,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index da882cf..42f1e7f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -38,6 +38,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -2178,7 +2179,7 @@ static inline int ufshcd_read_unit_desc_param(struct 
ufs_hba *hba,
 * Unit descriptors are only available for general purpose LUs (LUN id
 * from 0 to 7) and RPMB Well known LU.
 */
-   if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN))
+   if (!ufs_is_valid_unit_desc_lun(lun))
return -EOPNOTSUPP;
 
return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun,
@@ -5001,6 +5002,207 @@ out:
 }
 
 /**
+ * ufshcd_query_ioctl - perform user read queries
+ * @hba: per-adapter instance
+ * @lun: used for lun specific queries
+ * @buffer: user space buffer for reading and submitting query data and params
+ * @return: 0 for success negative error code otherwise
+ *
+ * Expected/Submitted buffer structure is struct ufs_ioctl_query_data.
+ * It will read the opcode, idn and buf_length parameters, and, put the
+ * response in the buffer field while updating the used size in buf_length.
+ */
+static 

[PATCH v6] scsi: ufs: add ioctl interface for query request

2016-03-09 Thread Yaniv Gardi
This patch exposes the ioctl interface for UFS driver via SCSI device
ioctl interface. As of now UFS driver would provide the ioctl for query
interface to connected UFS device.

Reviewed-by: Arnd Bergmann 
Reviewed-by: Subhash Jadavani 
Signed-off-by: Dolev Raviv 
Signed-off-by: Noa Rubens 
Signed-off-by: Raviv Shvili 
Signed-off-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 
---
 drivers/scsi/ufs/ufs.h|  53 +++
 drivers/scsi/ufs/ufshcd.c | 208 +-
 include/uapi/scsi/Kbuild  |   1 +
 include/uapi/scsi/ufs/Kbuild  |   3 +
 include/uapi/scsi/ufs/ioctl.h |  58 
 include/uapi/scsi/ufs/ufs.h   |  67 ++
 6 files changed, 347 insertions(+), 43 deletions(-)
 create mode 100644 include/uapi/scsi/ufs/Kbuild
 create mode 100644 include/uapi/scsi/ufs/ioctl.h
 create mode 100644 include/uapi/scsi/ufs/ufs.h

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index b291fa6..d39410f 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -38,6 +38,7 @@
 
 #include 
 #include 
+#include 
 
 #define MAX_CDB_SIZE   16
 #define GENERAL_UPIU_REQUEST_SIZE 32
@@ -72,6 +73,16 @@ enum {
UFS_UPIU_RPMB_WLUN  = 0xC4,
 };
 
+/**
+ * ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor
+ * @lun: LU number to check
+ * @return: true if the lun has a matching unit descriptor, false otherwise
+ */
+static inline bool ufs_is_valid_unit_desc_lun(u8 lun)
+{
+   return lun == UFS_UPIU_RPMB_WLUN || (lun < UFS_UPIU_MAX_GENERAL_LUN);
+}
+
 /*
  * UFS Protocol Information Unit related definitions
  */
@@ -127,35 +138,6 @@ enum {
UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST  = 0x81,
 };
 
-/* Flag idn for Query Requests*/
-enum flag_idn {
-   QUERY_FLAG_IDN_FDEVICEINIT  = 0x01,
-   QUERY_FLAG_IDN_PWR_ON_WPE   = 0x03,
-   QUERY_FLAG_IDN_BKOPS_EN = 0x04,
-};
-
-/* Attribute idn for Query requests */
-enum attr_idn {
-   QUERY_ATTR_IDN_ACTIVE_ICC_LVL   = 0x03,
-   QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
-   QUERY_ATTR_IDN_EE_CONTROL   = 0x0D,
-   QUERY_ATTR_IDN_EE_STATUS= 0x0E,
-};
-
-/* Descriptor idn for Query requests */
-enum desc_idn {
-   QUERY_DESC_IDN_DEVICE   = 0x0,
-   QUERY_DESC_IDN_CONFIGURAION = 0x1,
-   QUERY_DESC_IDN_UNIT = 0x2,
-   QUERY_DESC_IDN_RFU_0= 0x3,
-   QUERY_DESC_IDN_INTERCONNECT = 0x4,
-   QUERY_DESC_IDN_STRING   = 0x5,
-   QUERY_DESC_IDN_RFU_1= 0x6,
-   QUERY_DESC_IDN_GEOMETRY = 0x7,
-   QUERY_DESC_IDN_POWER= 0x8,
-   QUERY_DESC_IDN_MAX,
-};
-
 enum desc_header_offset {
QUERY_DESC_LENGTH_OFFSET= 0x00,
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
@@ -279,19 +261,6 @@ enum bkops_status {
BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL,
 };
 
-/* UTP QUERY Transaction Specific Fields OpCode */
-enum query_opcode {
-   UPIU_QUERY_OPCODE_NOP   = 0x0,
-   UPIU_QUERY_OPCODE_READ_DESC = 0x1,
-   UPIU_QUERY_OPCODE_WRITE_DESC= 0x2,
-   UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
-   UPIU_QUERY_OPCODE_WRITE_ATTR= 0x4,
-   UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
-   UPIU_QUERY_OPCODE_SET_FLAG  = 0x6,
-   UPIU_QUERY_OPCODE_CLEAR_FLAG= 0x7,
-   UPIU_QUERY_OPCODE_TOGGLE_FLAG   = 0x8,
-};
-
 /* Query response result code */
 enum {
QUERY_RESULT_SUCCESS= 0x00,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index da882cf..42f1e7f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -38,6 +38,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -2178,7 +2179,7 @@ static inline int ufshcd_read_unit_desc_param(struct 
ufs_hba *hba,
 * Unit descriptors are only available for general purpose LUs (LUN id
 * from 0 to 7) and RPMB Well known LU.
 */
-   if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN))
+   if (!ufs_is_valid_unit_desc_lun(lun))
return -EOPNOTSUPP;
 
return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun,
@@ -5001,6 +5002,207 @@ out:
 }
 
 /**
+ * ufshcd_query_ioctl - perform user read queries
+ * @hba: per-adapter instance
+ * @lun: used for lun specific queries
+ * @buffer: user space buffer for reading and submitting query data and params
+ * @return: 0 for success negative error code otherwise
+ *
+ * Expected/Submitted buffer structure is struct ufs_ioctl_query_data.
+ * It will read the opcode, idn and buf_length parameters, and, put the
+ * response in the buffer field while updating the used size in buf_length.
+ */
+static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer)
+{
+   struct ufs_ioctl_query_data ioctl_data;
+   int err = 0;
+   int length = 0;
+   void *data_ptr;
+  

[PATCH v5] scsi: ufs: add ioctl interface for query request

2016-03-09 Thread Yaniv Gardi
This patch exposes the ioctl interface for UFS driver via SCSI device
ioctl interface. As of now UFS driver would provide the ioctl for query
interface to connected UFS device.

Reviewed-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Dolev Raviv <dra...@codeaurora.org>
Signed-off-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs.h|  53 +++
 drivers/scsi/ufs/ufshcd.c | 208 +-
 include/uapi/scsi/Kbuild  |   1 +
 include/uapi/scsi/ufs/Kbuild  |   3 +
 include/uapi/scsi/ufs/ioctl.h |  58 
 include/uapi/scsi/ufs/ufs.h   |  67 ++
 6 files changed, 347 insertions(+), 43 deletions(-)
 create mode 100644 include/uapi/scsi/ufs/Kbuild
 create mode 100644 include/uapi/scsi/ufs/ioctl.h
 create mode 100644 include/uapi/scsi/ufs/ufs.h

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index b291fa6..d39410f 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -38,6 +38,7 @@
 
 #include 
 #include 
+#include 
 
 #define MAX_CDB_SIZE   16
 #define GENERAL_UPIU_REQUEST_SIZE 32
@@ -72,6 +73,16 @@ enum {
UFS_UPIU_RPMB_WLUN  = 0xC4,
 };
 
+/**
+ * ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor
+ * @lun: LU number to check
+ * @return: true if the lun has a matching unit descriptor, false otherwise
+ */
+static inline bool ufs_is_valid_unit_desc_lun(u8 lun)
+{
+   return lun == UFS_UPIU_RPMB_WLUN || (lun < UFS_UPIU_MAX_GENERAL_LUN);
+}
+
 /*
  * UFS Protocol Information Unit related definitions
  */
@@ -127,35 +138,6 @@ enum {
UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST  = 0x81,
 };
 
-/* Flag idn for Query Requests*/
-enum flag_idn {
-   QUERY_FLAG_IDN_FDEVICEINIT  = 0x01,
-   QUERY_FLAG_IDN_PWR_ON_WPE   = 0x03,
-   QUERY_FLAG_IDN_BKOPS_EN = 0x04,
-};
-
-/* Attribute idn for Query requests */
-enum attr_idn {
-   QUERY_ATTR_IDN_ACTIVE_ICC_LVL   = 0x03,
-   QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
-   QUERY_ATTR_IDN_EE_CONTROL   = 0x0D,
-   QUERY_ATTR_IDN_EE_STATUS= 0x0E,
-};
-
-/* Descriptor idn for Query requests */
-enum desc_idn {
-   QUERY_DESC_IDN_DEVICE   = 0x0,
-   QUERY_DESC_IDN_CONFIGURAION = 0x1,
-   QUERY_DESC_IDN_UNIT = 0x2,
-   QUERY_DESC_IDN_RFU_0= 0x3,
-   QUERY_DESC_IDN_INTERCONNECT = 0x4,
-   QUERY_DESC_IDN_STRING   = 0x5,
-   QUERY_DESC_IDN_RFU_1= 0x6,
-   QUERY_DESC_IDN_GEOMETRY = 0x7,
-   QUERY_DESC_IDN_POWER= 0x8,
-   QUERY_DESC_IDN_MAX,
-};
-
 enum desc_header_offset {
QUERY_DESC_LENGTH_OFFSET= 0x00,
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
@@ -279,19 +261,6 @@ enum bkops_status {
BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL,
 };
 
-/* UTP QUERY Transaction Specific Fields OpCode */
-enum query_opcode {
-   UPIU_QUERY_OPCODE_NOP   = 0x0,
-   UPIU_QUERY_OPCODE_READ_DESC = 0x1,
-   UPIU_QUERY_OPCODE_WRITE_DESC= 0x2,
-   UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
-   UPIU_QUERY_OPCODE_WRITE_ATTR= 0x4,
-   UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
-   UPIU_QUERY_OPCODE_SET_FLAG  = 0x6,
-   UPIU_QUERY_OPCODE_CLEAR_FLAG= 0x7,
-   UPIU_QUERY_OPCODE_TOGGLE_FLAG   = 0x8,
-};
-
 /* Query response result code */
 enum {
QUERY_RESULT_SUCCESS= 0x00,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index da882cf..42f1e7f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -38,6 +38,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -2178,7 +2179,7 @@ static inline int ufshcd_read_unit_desc_param(struct 
ufs_hba *hba,
 * Unit descriptors are only available for general purpose LUs (LUN id
 * from 0 to 7) and RPMB Well known LU.
 */
-   if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN))
+   if (!ufs_is_valid_unit_desc_lun(lun))
return -EOPNOTSUPP;
 
return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun,
@@ -5001,6 +5002,207 @@ out:
 }
 
 /**
+ * ufshcd_query_ioctl - perform user read queries
+ * @hba: per-adapter instance
+ * @lun: used for lun specific queries
+ * @buffer: user space buffer for reading and submitting query data and params
+ * @return: 0 for success negative error code otherwise
+ *
+ * Expected/Submitted buffer structure is struct ufs_ioctl_query_data.
+ * It will read the opcode, idn and buf_length parameters, and, put the
+ * response in the buffer field while updating the used size in buf_length.
+ */
+static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer)
+{
+   struct ufs_ioctl_query_data ioctl_data;
+   int err = 0;
+   int length = 0

[PATCH v5] scsi: ufs: add ioctl interface for query request

2016-03-09 Thread Yaniv Gardi
This patch exposes the ioctl interface for UFS driver via SCSI device
ioctl interface. As of now UFS driver would provide the ioctl for query
interface to connected UFS device.

Reviewed-by: Subhash Jadavani 
Signed-off-by: Dolev Raviv 
Signed-off-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs.h|  53 +++
 drivers/scsi/ufs/ufshcd.c | 208 +-
 include/uapi/scsi/Kbuild  |   1 +
 include/uapi/scsi/ufs/Kbuild  |   3 +
 include/uapi/scsi/ufs/ioctl.h |  58 
 include/uapi/scsi/ufs/ufs.h   |  67 ++
 6 files changed, 347 insertions(+), 43 deletions(-)
 create mode 100644 include/uapi/scsi/ufs/Kbuild
 create mode 100644 include/uapi/scsi/ufs/ioctl.h
 create mode 100644 include/uapi/scsi/ufs/ufs.h

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index b291fa6..d39410f 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -38,6 +38,7 @@
 
 #include 
 #include 
+#include 
 
 #define MAX_CDB_SIZE   16
 #define GENERAL_UPIU_REQUEST_SIZE 32
@@ -72,6 +73,16 @@ enum {
UFS_UPIU_RPMB_WLUN  = 0xC4,
 };
 
+/**
+ * ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor
+ * @lun: LU number to check
+ * @return: true if the lun has a matching unit descriptor, false otherwise
+ */
+static inline bool ufs_is_valid_unit_desc_lun(u8 lun)
+{
+   return lun == UFS_UPIU_RPMB_WLUN || (lun < UFS_UPIU_MAX_GENERAL_LUN);
+}
+
 /*
  * UFS Protocol Information Unit related definitions
  */
@@ -127,35 +138,6 @@ enum {
UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST  = 0x81,
 };
 
-/* Flag idn for Query Requests*/
-enum flag_idn {
-   QUERY_FLAG_IDN_FDEVICEINIT  = 0x01,
-   QUERY_FLAG_IDN_PWR_ON_WPE   = 0x03,
-   QUERY_FLAG_IDN_BKOPS_EN = 0x04,
-};
-
-/* Attribute idn for Query requests */
-enum attr_idn {
-   QUERY_ATTR_IDN_ACTIVE_ICC_LVL   = 0x03,
-   QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
-   QUERY_ATTR_IDN_EE_CONTROL   = 0x0D,
-   QUERY_ATTR_IDN_EE_STATUS= 0x0E,
-};
-
-/* Descriptor idn for Query requests */
-enum desc_idn {
-   QUERY_DESC_IDN_DEVICE   = 0x0,
-   QUERY_DESC_IDN_CONFIGURAION = 0x1,
-   QUERY_DESC_IDN_UNIT = 0x2,
-   QUERY_DESC_IDN_RFU_0= 0x3,
-   QUERY_DESC_IDN_INTERCONNECT = 0x4,
-   QUERY_DESC_IDN_STRING   = 0x5,
-   QUERY_DESC_IDN_RFU_1= 0x6,
-   QUERY_DESC_IDN_GEOMETRY = 0x7,
-   QUERY_DESC_IDN_POWER= 0x8,
-   QUERY_DESC_IDN_MAX,
-};
-
 enum desc_header_offset {
QUERY_DESC_LENGTH_OFFSET= 0x00,
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
@@ -279,19 +261,6 @@ enum bkops_status {
BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL,
 };
 
-/* UTP QUERY Transaction Specific Fields OpCode */
-enum query_opcode {
-   UPIU_QUERY_OPCODE_NOP   = 0x0,
-   UPIU_QUERY_OPCODE_READ_DESC = 0x1,
-   UPIU_QUERY_OPCODE_WRITE_DESC= 0x2,
-   UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
-   UPIU_QUERY_OPCODE_WRITE_ATTR= 0x4,
-   UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
-   UPIU_QUERY_OPCODE_SET_FLAG  = 0x6,
-   UPIU_QUERY_OPCODE_CLEAR_FLAG= 0x7,
-   UPIU_QUERY_OPCODE_TOGGLE_FLAG   = 0x8,
-};
-
 /* Query response result code */
 enum {
QUERY_RESULT_SUCCESS= 0x00,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index da882cf..42f1e7f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -38,6 +38,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -2178,7 +2179,7 @@ static inline int ufshcd_read_unit_desc_param(struct 
ufs_hba *hba,
 * Unit descriptors are only available for general purpose LUs (LUN id
 * from 0 to 7) and RPMB Well known LU.
 */
-   if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN))
+   if (!ufs_is_valid_unit_desc_lun(lun))
return -EOPNOTSUPP;
 
return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun,
@@ -5001,6 +5002,207 @@ out:
 }
 
 /**
+ * ufshcd_query_ioctl - perform user read queries
+ * @hba: per-adapter instance
+ * @lun: used for lun specific queries
+ * @buffer: user space buffer for reading and submitting query data and params
+ * @return: 0 for success negative error code otherwise
+ *
+ * Expected/Submitted buffer structure is struct ufs_ioctl_query_data.
+ * It will read the opcode, idn and buf_length parameters, and, put the
+ * response in the buffer field while updating the used size in buf_length.
+ */
+static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer)
+{
+   struct ufs_ioctl_query_data ioctl_data;
+   int err = 0;
+   int length = 0;
+   void *data_ptr;
+   bool flag;
+   u32 att;
+   u8 index;
+   u8 *desc = NULL;
+
+   /* 

[PATCH v7 03/17] scsi: ufs: implement scsi host timeout handler

2016-03-08 Thread Yaniv Gardi
A race condition exists between request requeueing and scsi layer
error handling:
When UFS driver queuecommand returns a busy status for a request,
it will be requeued and its tag will be freed and set to -1.
At the same time it is possible that the request will timeout and
scsi layer will start error handling for it. The scsi layer reuses
the request and its tag to send error related commands to the device,
however its tag is no longer valid.
As this request was never really sent to the device, there is no
point to start error handling with the device.
Implement the scsi error handling timeout callback and bypass SCSI
error handling for request that were not actually sent to the device.
For such requests simply reset the block layer timer. Otherwise, let
SCSI layer perform the usual error handling.

Reviewed-by: Dolev Raviv <dra...@codeaurora.org>
Signed-off-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 36 
 1 file changed, 36 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index de7280c..3400ceb 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -4568,6 +4568,41 @@ static void ufshcd_async_scan(void *data, async_cookie_t 
cookie)
ufshcd_probe_hba(hba);
 }
 
+static enum blk_eh_timer_return ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
+{
+   unsigned long flags;
+   struct Scsi_Host *host;
+   struct ufs_hba *hba;
+   int index;
+   bool found = false;
+
+   if (!scmd || !scmd->device || !scmd->device->host)
+   return BLK_EH_NOT_HANDLED;
+
+   host = scmd->device->host;
+   hba = shost_priv(host);
+   if (!hba)
+   return BLK_EH_NOT_HANDLED;
+
+   spin_lock_irqsave(host->host_lock, flags);
+
+   for_each_set_bit(index, >outstanding_reqs, hba->nutrs) {
+   if (hba->lrb[index].cmd == scmd) {
+   found = true;
+   break;
+   }
+   }
+
+   spin_unlock_irqrestore(host->host_lock, flags);
+
+   /*
+* Bypass SCSI error handling and reset the block layer timer if this
+* SCSI command was not actually dispatched to UFS driver, otherwise
+* let SCSI layer handle the error as usual.
+*/
+   return found ? BLK_EH_NOT_HANDLED : BLK_EH_RESET_TIMER;
+}
+
 static struct scsi_host_template ufshcd_driver_template = {
.module = THIS_MODULE,
.name   = UFSHCD,
@@ -4580,6 +4615,7 @@ static struct scsi_host_template ufshcd_driver_template = 
{
.eh_abort_handler   = ufshcd_abort,
.eh_device_reset_handler = ufshcd_eh_device_reset_handler,
.eh_host_reset_handler   = ufshcd_eh_host_reset_handler,
+   .eh_timed_out   = ufshcd_eh_timed_out,
.this_id= -1,
.sg_tablesize   = SG_ALL,
.cmd_per_lun= UFSHCD_CMD_PER_LUN,
-- 
1.8.5.2

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


[PATCH v7 03/17] scsi: ufs: implement scsi host timeout handler

2016-03-08 Thread Yaniv Gardi
A race condition exists between request requeueing and scsi layer
error handling:
When UFS driver queuecommand returns a busy status for a request,
it will be requeued and its tag will be freed and set to -1.
At the same time it is possible that the request will timeout and
scsi layer will start error handling for it. The scsi layer reuses
the request and its tag to send error related commands to the device,
however its tag is no longer valid.
As this request was never really sent to the device, there is no
point to start error handling with the device.
Implement the scsi error handling timeout callback and bypass SCSI
error handling for request that were not actually sent to the device.
For such requests simply reset the block layer timer. Otherwise, let
SCSI layer perform the usual error handling.

Reviewed-by: Dolev Raviv 
Signed-off-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 36 
 1 file changed, 36 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index de7280c..3400ceb 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -4568,6 +4568,41 @@ static void ufshcd_async_scan(void *data, async_cookie_t 
cookie)
ufshcd_probe_hba(hba);
 }
 
+static enum blk_eh_timer_return ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
+{
+   unsigned long flags;
+   struct Scsi_Host *host;
+   struct ufs_hba *hba;
+   int index;
+   bool found = false;
+
+   if (!scmd || !scmd->device || !scmd->device->host)
+   return BLK_EH_NOT_HANDLED;
+
+   host = scmd->device->host;
+   hba = shost_priv(host);
+   if (!hba)
+   return BLK_EH_NOT_HANDLED;
+
+   spin_lock_irqsave(host->host_lock, flags);
+
+   for_each_set_bit(index, >outstanding_reqs, hba->nutrs) {
+   if (hba->lrb[index].cmd == scmd) {
+   found = true;
+   break;
+   }
+   }
+
+   spin_unlock_irqrestore(host->host_lock, flags);
+
+   /*
+* Bypass SCSI error handling and reset the block layer timer if this
+* SCSI command was not actually dispatched to UFS driver, otherwise
+* let SCSI layer handle the error as usual.
+*/
+   return found ? BLK_EH_NOT_HANDLED : BLK_EH_RESET_TIMER;
+}
+
 static struct scsi_host_template ufshcd_driver_template = {
.module = THIS_MODULE,
.name   = UFSHCD,
@@ -4580,6 +4615,7 @@ static struct scsi_host_template ufshcd_driver_template = 
{
.eh_abort_handler   = ufshcd_abort,
.eh_device_reset_handler = ufshcd_eh_device_reset_handler,
.eh_host_reset_handler   = ufshcd_eh_host_reset_handler,
+   .eh_timed_out   = ufshcd_eh_timed_out,
.this_id= -1,
.sg_tablesize   = SG_ALL,
.cmd_per_lun= UFSHCD_CMD_PER_LUN,
-- 
1.8.5.2

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


[PATCH v7 07/17] scsi: ufs: disable vccq if it's not needed by UFS device

2016-03-08 Thread Yaniv Gardi
Some UFS devices don't require VCCQ rail for device operations hence
this change adds support to recognize such devices and remove vote for
the unused VCCQ rail.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs.h|  1 +
 drivers/scsi/ufs/ufshcd.c | 60 +++
 2 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 8ec6356..b291fa6 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -501,6 +501,7 @@ struct ufs_vreg {
struct regulator *reg;
const char *name;
bool enabled;
+   bool unused;
int min_uV;
int max_uV;
int min_uA;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 91a341b..987cf27 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -195,6 +195,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba);
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
 bool skip_ref_clk);
 static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused);
 static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba);
 static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
 static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
@@ -4593,6 +4594,12 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
goto out;
 
ufs_advertise_fixup_device(hba);
+
+   ret = ufshcd_set_vccq_rail_unused(hba,
+   (hba->dev_quirks & UFS_DEVICE_NO_VCCQ) ? true : false);
+   if (ret)
+   goto out;
+
/* UFS device is also active now */
ufshcd_set_ufs_dev_active(hba);
ufshcd_force_reset_auto_bkops(hba);
@@ -4743,13 +4750,24 @@ static int ufshcd_config_vreg_load(struct device *dev, 
struct ufs_vreg *vreg,
 static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg,
+  UFS_VREG_LPM_LOAD_UA);
 }
 
 static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
 }
 
 static int ufshcd_config_vreg(struct device *dev,
@@ -4784,7 +4802,9 @@ static int ufshcd_enable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (vreg->enabled || vreg->unused)
goto out;
 
ret = ufshcd_config_vreg(dev, vreg, true);
@@ -4804,7 +4824,9 @@ static int ufshcd_disable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || !vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (!vreg->enabled || vreg->unused)
goto out;
 
ret = regulator_disable(vreg->reg);
@@ -4910,6 +4932,36 @@ static int ufshcd_init_hba_vreg(struct ufs_hba *hba)
return 0;
 }
 
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused)
+{
+   int ret = 0;
+   struct ufs_vreg_info *info = >vreg_info;
+
+   if (!info)
+   goto out;
+   else if (!info->vccq)
+   goto out;
+
+   if (unused) {
+   /* shut off the rail here */
+   ret = ufshcd_toggle_vreg(hba->dev, info->vccq, false);
+   /*
+* Mark this rail as no longer used, so it doesn't get enabled
+* later by mistake
+*/
+   if (!ret)
+   info->vccq->unused = true;
+   } else {
+   /*
+* rail should have been already enabled hence just make sure
+* that unused flag is cleared.
+*/
+   info->vccq->unused = false;
+   }
+out:
+   return ret;
+}
+
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
bool skip_ref_clk)
 {
-- 
1.8.5.2

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


[PATCH v7 07/17] scsi: ufs: disable vccq if it's not needed by UFS device

2016-03-08 Thread Yaniv Gardi
Some UFS devices don't require VCCQ rail for device operations hence
this change adds support to recognize such devices and remove vote for
the unused VCCQ rail.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs.h|  1 +
 drivers/scsi/ufs/ufshcd.c | 60 +++
 2 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 8ec6356..b291fa6 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -501,6 +501,7 @@ struct ufs_vreg {
struct regulator *reg;
const char *name;
bool enabled;
+   bool unused;
int min_uV;
int max_uV;
int min_uA;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 91a341b..987cf27 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -195,6 +195,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba);
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
 bool skip_ref_clk);
 static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused);
 static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba);
 static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
 static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
@@ -4593,6 +4594,12 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
goto out;
 
ufs_advertise_fixup_device(hba);
+
+   ret = ufshcd_set_vccq_rail_unused(hba,
+   (hba->dev_quirks & UFS_DEVICE_NO_VCCQ) ? true : false);
+   if (ret)
+   goto out;
+
/* UFS device is also active now */
ufshcd_set_ufs_dev_active(hba);
ufshcd_force_reset_auto_bkops(hba);
@@ -4743,13 +4750,24 @@ static int ufshcd_config_vreg_load(struct device *dev, 
struct ufs_vreg *vreg,
 static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg,
+  UFS_VREG_LPM_LOAD_UA);
 }
 
 static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
 }
 
 static int ufshcd_config_vreg(struct device *dev,
@@ -4784,7 +4802,9 @@ static int ufshcd_enable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (vreg->enabled || vreg->unused)
goto out;
 
ret = ufshcd_config_vreg(dev, vreg, true);
@@ -4804,7 +4824,9 @@ static int ufshcd_disable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || !vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (!vreg->enabled || vreg->unused)
goto out;
 
ret = regulator_disable(vreg->reg);
@@ -4910,6 +4932,36 @@ static int ufshcd_init_hba_vreg(struct ufs_hba *hba)
return 0;
 }
 
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused)
+{
+   int ret = 0;
+   struct ufs_vreg_info *info = >vreg_info;
+
+   if (!info)
+   goto out;
+   else if (!info->vccq)
+   goto out;
+
+   if (unused) {
+   /* shut off the rail here */
+   ret = ufshcd_toggle_vreg(hba->dev, info->vccq, false);
+   /*
+* Mark this rail as no longer used, so it doesn't get enabled
+* later by mistake
+*/
+   if (!ret)
+   info->vccq->unused = true;
+   } else {
+   /*
+* rail should have been already enabled hence just make sure
+* that unused flag is cleared.
+*/
+   info->vccq->unused = false;
+   }
+out:
+   return ret;
+}
+
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
bool skip_ref_clk)
 {
-- 
1.8.5.2

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


[PATCH v7 01/17] scsi: ufs-qcom: add number of lanes per direction

2016-03-08 Thread Yaniv Gardi
Different platform may have different number of lanes
for the UFS link.
Add parameter to device tree specifying how many lanes
should be configured for the UFS link.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Acked-by: Rob Herring <r...@kernel.org>
Signed-off-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |  3 ++
 drivers/scsi/ufs/ufs-qcom.c| 39 --
 drivers/scsi/ufs/ufshcd-pltfrm.c   | 19 +++
 drivers/scsi/ufs/ufshcd.c  |  1 +
 drivers/scsi/ufs/ufshcd.h  |  2 ++
 5 files changed, 47 insertions(+), 17 deletions(-)

diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt 
b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 03c0e98..66f6adf 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -38,6 +38,9 @@ Optional properties:
  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.
+-lanes-per-direction   : number of lanes available per direction - either 1 or 
2.
+ Note that it is assume same number of lanes is used 
both
+ directions at once. If not specified, default is 2 
lanes per direction.
 
 Note: If above properties are not defined it can be assumed that the supply
 regulators or clocks are always on.
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 4f38d00..ed57729 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016, Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -132,21 +132,24 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host 
*host)
if (err)
goto disable_rx_l0;
 
-   err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk",
-   host->rx_l1_sync_clk);
-   if (err)
-   goto disable_tx_l0;
+   if (host->hba->lanes_per_direction > 1) {
+   err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk",
+   host->rx_l1_sync_clk);
+   if (err)
+   goto disable_tx_l0;
 
-   err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk",
-   host->tx_l1_sync_clk);
-   if (err)
-   goto disable_rx_l1;
+   err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk",
+   host->tx_l1_sync_clk);
+   if (err)
+   goto disable_rx_l1;
+   }
 
host->is_lane_clks_enabled = true;
goto out;
 
 disable_rx_l1:
-   clk_disable_unprepare(host->rx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->rx_l1_sync_clk);
 disable_tx_l0:
clk_disable_unprepare(host->tx_l0_sync_clk);
 disable_rx_l0:
@@ -170,14 +173,16 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host 
*host)
if (err)
goto out;
 
-   err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
-   >rx_l1_sync_clk);
-   if (err)
-   goto out;
-
-   err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
-   >tx_l1_sync_clk);
+   /* In case of single lane per direction, don't read lane1 clocks */
+   if (host->hba->lanes_per_direction > 1) {
+   err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
+   >rx_l1_sync_clk);
+   if (err)
+   goto out;
 
+   err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
+   >tx_l1_sync_clk);
+   }
 out:
return err;
 }
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index d2a7b12..718f12e 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -40,6 +40,8 @@
 #include "ufshcd.h"
 #include "ufshcd-pltfrm.h"
 
+#define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2
+
 static int ufshcd_parse_clock_info(struct ufs_hba *hba)
 {
int ret = 0;
@@ -277,6 +279,21 @@ void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
 }
 EXPORT_SYMBOL_GPL(ufshcd_pltfrm_shutdown);
 
+static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
+{
+   struct device *

[PATCH v7 01/17] scsi: ufs-qcom: add number of lanes per direction

2016-03-08 Thread Yaniv Gardi
Different platform may have different number of lanes
for the UFS link.
Add parameter to device tree specifying how many lanes
should be configured for the UFS link.

Reviewed-by: Hannes Reinecke 
Acked-by: Rob Herring 
Signed-off-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |  3 ++
 drivers/scsi/ufs/ufs-qcom.c| 39 --
 drivers/scsi/ufs/ufshcd-pltfrm.c   | 19 +++
 drivers/scsi/ufs/ufshcd.c  |  1 +
 drivers/scsi/ufs/ufshcd.h  |  2 ++
 5 files changed, 47 insertions(+), 17 deletions(-)

diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt 
b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 03c0e98..66f6adf 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -38,6 +38,9 @@ Optional properties:
  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.
+-lanes-per-direction   : number of lanes available per direction - either 1 or 
2.
+ Note that it is assume same number of lanes is used 
both
+ directions at once. If not specified, default is 2 
lanes per direction.
 
 Note: If above properties are not defined it can be assumed that the supply
 regulators or clocks are always on.
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 4f38d00..ed57729 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016, Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -132,21 +132,24 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host 
*host)
if (err)
goto disable_rx_l0;
 
-   err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk",
-   host->rx_l1_sync_clk);
-   if (err)
-   goto disable_tx_l0;
+   if (host->hba->lanes_per_direction > 1) {
+   err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk",
+   host->rx_l1_sync_clk);
+   if (err)
+   goto disable_tx_l0;
 
-   err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk",
-   host->tx_l1_sync_clk);
-   if (err)
-   goto disable_rx_l1;
+   err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk",
+   host->tx_l1_sync_clk);
+   if (err)
+   goto disable_rx_l1;
+   }
 
host->is_lane_clks_enabled = true;
goto out;
 
 disable_rx_l1:
-   clk_disable_unprepare(host->rx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->rx_l1_sync_clk);
 disable_tx_l0:
clk_disable_unprepare(host->tx_l0_sync_clk);
 disable_rx_l0:
@@ -170,14 +173,16 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host 
*host)
if (err)
goto out;
 
-   err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
-   >rx_l1_sync_clk);
-   if (err)
-   goto out;
-
-   err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
-   >tx_l1_sync_clk);
+   /* In case of single lane per direction, don't read lane1 clocks */
+   if (host->hba->lanes_per_direction > 1) {
+   err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
+   >rx_l1_sync_clk);
+   if (err)
+   goto out;
 
+   err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
+   >tx_l1_sync_clk);
+   }
 out:
return err;
 }
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index d2a7b12..718f12e 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -40,6 +40,8 @@
 #include "ufshcd.h"
 #include "ufshcd-pltfrm.h"
 
+#define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2
+
 static int ufshcd_parse_clock_info(struct ufs_hba *hba)
 {
int ret = 0;
@@ -277,6 +279,21 @@ void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
 }
 EXPORT_SYMBOL_GPL(ufshcd_pltfrm_shutdown);
 
+static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
+{
+   struct device *dev = hba->dev;
+   int ret;
+
+   ret = of_property_read_u32(dev->of_node, "lan

[PATCH v7 12/17] scsi: ufs: tune UniPro parameters to optimize hibern8 exit time

2016-03-08 Thread Yaniv Gardi
Optimal values of local UniPro parameters like PA_Hibern8Time &
PA_TActivate can help reduce the hibern8 exit latency. If both host and
device supports UniPro ver1.6 or later, these parameters will be
automatically tuned during link startup itself. But if either host or
device doesn't support UniPro ver 1.6 or later, we have to manually
tune them. But to keep manual tuning logic simple, we will only do
manual tuning if local unipro version doesn't support ver1.6 or later.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 118 ++
 drivers/scsi/ufs/ufshcd.h |   1 +
 drivers/scsi/ufs/ufshci.h |   2 +
 drivers/scsi/ufs/unipro.h |  21 +
 4 files changed, 142 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index d28c629..c9c4d68 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -570,6 +570,34 @@ static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
 }
 
+u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba)
+{
+   /* HCI version 1.0 and 1.1 supports UniPro 1.41 */
+   if ((hba->ufs_version == UFSHCI_VERSION_10) ||
+   (hba->ufs_version == UFSHCI_VERSION_11))
+   return UFS_UNIPRO_VER_1_41;
+   else
+   return UFS_UNIPRO_VER_1_6;
+}
+EXPORT_SYMBOL(ufshcd_get_local_unipro_ver);
+
+static bool ufshcd_is_unipro_pa_params_tuning_req(struct ufs_hba *hba)
+{
+   /*
+* If both host and device support UniPro ver1.6 or later, PA layer
+* parameters tuning happens during link startup itself.
+*
+* We can manually tune PA layer parameters if either host or device
+* doesn't support UniPro ver 1.6 or later. But to keep manual tuning
+* logic simple, we will only do manual tuning if local unipro version
+* doesn't support ver1.6 or later.
+*/
+   if (ufshcd_get_local_unipro_ver(hba) < UFS_UNIPRO_VER_1_6)
+   return true;
+   else
+   return false;
+}
+
 static void ufshcd_ungate_work(struct work_struct *work)
 {
int ret;
@@ -4783,6 +4811,95 @@ out:
 }
 
 /**
+ * ufshcd_tune_pa_tactivate - Tunes PA_TActivate of local UniPro
+ * @hba: per-adapter instance
+ *
+ * PA_TActivate parameter can be tuned manually if UniPro version is less than
+ * 1.61. PA_TActivate needs to be greater than or equal to peerM-PHY's
+ * RX_MIN_ACTIVATETIME_CAPABILITY attribute. This optimal value can help reduce
+ * the hibern8 exit latency.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_tune_pa_tactivate(struct ufs_hba *hba)
+{
+   int ret = 0;
+   u32 peer_rx_min_activatetime = 0, tuned_pa_tactivate;
+
+   ret = ufshcd_dme_peer_get(hba,
+ UIC_ARG_MIB_SEL(
+   RX_MIN_ACTIVATETIME_CAPABILITY,
+   UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
+ _rx_min_activatetime);
+   if (ret)
+   goto out;
+
+   /* make sure proper unit conversion is applied */
+   tuned_pa_tactivate =
+   ((peer_rx_min_activatetime * RX_MIN_ACTIVATETIME_UNIT_US)
+/ PA_TACTIVATE_TIME_UNIT_US);
+   ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE),
+tuned_pa_tactivate);
+
+out:
+   return ret;
+}
+
+/**
+ * ufshcd_tune_pa_hibern8time - Tunes PA_Hibern8Time of local UniPro
+ * @hba: per-adapter instance
+ *
+ * PA_Hibern8Time parameter can be tuned manually if UniPro version is less 
than
+ * 1.61. PA_Hibern8Time needs to be maximum of local M-PHY's
+ * TX_HIBERN8TIME_CAPABILITY & peer M-PHY's RX_HIBERN8TIME_CAPABILITY.
+ * This optimal value can help reduce the hibern8 exit latency.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_tune_pa_hibern8time(struct ufs_hba *hba)
+{
+   int ret = 0;
+   u32 local_tx_hibern8_time_cap = 0, peer_rx_hibern8_time_cap = 0;
+   u32 max_hibern8_time, tuned_pa_hibern8time;
+
+   ret = ufshcd_dme_get(hba,
+UIC_ARG_MIB_SEL(TX_HIBERN8TIME_CAPABILITY,
+   UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
+ _tx_hibern8_time_cap);
+   if (ret)
+   goto out;
+
+   ret = ufshcd_dme_peer_get(hba,
+ UIC_ARG_MIB_SEL(RX_HIBERN8TIME_CAPABILITY,
+   UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
+ _rx_hibern8_time_cap);
+   if (ret)
+   goto out;
+
+   max_hibern8_time = max(local_tx_hibern8_time_cap,
+   

[PATCH v7 08/17] scsi: ufs: make error handling bit faster

2016-03-08 Thread Yaniv Gardi
UFS driver's error handler forcefully tries to clear all the pending
requests. For each pending request in the queue, it waits 1 sec for it
to get cleared. If we have multiple requests in the queue then it's
possible that we might end up waiting for those many seconds before
resetting the host. But note that resetting host would any way clear
all the pending requests from the hardware. Hence this change skips
the forceful clear of the pending requests if we are anyway going to
reset the host (for fatal errors).

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 155 +-
 1 file changed, 112 insertions(+), 43 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 987cf27..dc096f1 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -133,9 +133,11 @@ enum {
 /* 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 */
+   UFSHCD_UIC_DL_NAC_RECEIVED_ERROR = (1 << 1), /* Data link layer error */
+   UFSHCD_UIC_DL_TCx_REPLAY_ERROR = (1 << 2), /* Data link layer error */
+   UFSHCD_UIC_NL_ERROR = (1 << 3), /* Network layer error */
+   UFSHCD_UIC_TL_ERROR = (1 << 4), /* Transport Layer error */
+   UFSHCD_UIC_DME_ERROR = (1 << 5), /* DME error */
 };
 
 /* Interrupt configuration options */
@@ -3465,31 +3467,18 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, 
u32 intr_status)
 }
 
 /**
- * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * __ufshcd_transfer_req_compl - handle SCSI and query command completion
  * @hba: per adapter instance
+ * @completed_reqs: requests to complete
  */
-static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
+   unsigned long completed_reqs)
 {
struct ufshcd_lrb *lrbp;
struct scsi_cmnd *cmd;
-   unsigned long completed_reqs;
-   u32 tr_doorbell;
int result;
int index;
 
-   /* Resetting interrupt aggregation counters first and reading the
-* DOOR_BELL afterward allows us to handle all the completed requests.
-* In order to prevent other interrupts starvation the DB is read once
-* after reset. The down side of this solution is the possibility of
-* false interrupt if device completes another request after resetting
-* aggregation and before reading the DB.
-*/
-   if (ufshcd_is_intr_aggr_allowed(hba))
-   ufshcd_reset_intr_aggr(hba);
-
-   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
-   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
-
for_each_set_bit(index, _reqs, hba->nutrs) {
lrbp = >lrb[index];
cmd = lrbp->cmd;
@@ -3519,6 +3508,31 @@ static void ufshcd_transfer_req_compl(struct ufs_hba 
*hba)
 }
 
 /**
+ * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+{
+   unsigned long completed_reqs;
+   u32 tr_doorbell;
+
+   /* Resetting interrupt aggregation counters first and reading the
+* DOOR_BELL afterward allows us to handle all the completed requests.
+* In order to prevent other interrupts starvation the DB is read once
+* after reset. The down side of this solution is the possibility of
+* false interrupt if device completes another request after resetting
+* aggregation and before reading the DB.
+*/
+   if (ufshcd_is_intr_aggr_allowed(hba))
+   ufshcd_reset_intr_aggr(hba);
+
+   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
+
+   __ufshcd_transfer_req_compl(hba, completed_reqs);
+}
+
+/**
  * ufshcd_disable_ee - disable exception event
  * @hba: per-adapter instance
  * @mask: exception event to disable
@@ -3773,6 +3787,13 @@ out:
return;
 }
 
+/* Complete requests that have door-bell cleared */
+static void ufshcd_complete_requests(struct ufs_hba *hba)
+{
+   ufshcd_transfer_req_compl(hba);
+   ufshcd_tmc_handler(hba);
+}
+
 /**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
@@ -3785,6 +3806,7 @@ static void ufshcd_err_handler(struct work_struct *work)
u32 err_tm = 0;
int err = 0;
int tag;
+

[PATCH v7 13/17] scsi: ufs: fix leakage during link off state

2016-03-08 Thread Yaniv Gardi
Currently when we try to put the link in off/disabled state during
suspend, it seems link is not being kept in low power mode.
This patch fixes the issue by putting the link in hibern8 first
(so device also puts the link in low power mode) and then stop the
host controller.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c9c4d68..f108a0b 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5619,6 +5619,16 @@ static int ufshcd_link_state_transition(struct ufs_hba 
*hba,
   (!check_for_bkops || (check_for_bkops &&
!hba->auto_bkops_enabled))) {
/*
+* Let's make sure that link is in low power mode, we are doing
+* this currently by putting the link in Hibern8. Otherway to
+* put the link in low power mode is to send the DME end point
+* to device and then send the DME reset command to local
+* unipro. But putting the link in hibern8 is much faster.
+*/
+   ret = ufshcd_uic_hibern8_enter(hba);
+   if (ret)
+   goto out;
+   /*
 * Change controller state to "reset state" which
 * should also put the link in off/reset state
 */
-- 
1.8.5.2

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


[PATCH v7 12/17] scsi: ufs: tune UniPro parameters to optimize hibern8 exit time

2016-03-08 Thread Yaniv Gardi
Optimal values of local UniPro parameters like PA_Hibern8Time &
PA_TActivate can help reduce the hibern8 exit latency. If both host and
device supports UniPro ver1.6 or later, these parameters will be
automatically tuned during link startup itself. But if either host or
device doesn't support UniPro ver 1.6 or later, we have to manually
tune them. But to keep manual tuning logic simple, we will only do
manual tuning if local unipro version doesn't support ver1.6 or later.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 118 ++
 drivers/scsi/ufs/ufshcd.h |   1 +
 drivers/scsi/ufs/ufshci.h |   2 +
 drivers/scsi/ufs/unipro.h |  21 +
 4 files changed, 142 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index d28c629..c9c4d68 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -570,6 +570,34 @@ static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
 }
 
+u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba)
+{
+   /* HCI version 1.0 and 1.1 supports UniPro 1.41 */
+   if ((hba->ufs_version == UFSHCI_VERSION_10) ||
+   (hba->ufs_version == UFSHCI_VERSION_11))
+   return UFS_UNIPRO_VER_1_41;
+   else
+   return UFS_UNIPRO_VER_1_6;
+}
+EXPORT_SYMBOL(ufshcd_get_local_unipro_ver);
+
+static bool ufshcd_is_unipro_pa_params_tuning_req(struct ufs_hba *hba)
+{
+   /*
+* If both host and device support UniPro ver1.6 or later, PA layer
+* parameters tuning happens during link startup itself.
+*
+* We can manually tune PA layer parameters if either host or device
+* doesn't support UniPro ver 1.6 or later. But to keep manual tuning
+* logic simple, we will only do manual tuning if local unipro version
+* doesn't support ver1.6 or later.
+*/
+   if (ufshcd_get_local_unipro_ver(hba) < UFS_UNIPRO_VER_1_6)
+   return true;
+   else
+   return false;
+}
+
 static void ufshcd_ungate_work(struct work_struct *work)
 {
int ret;
@@ -4783,6 +4811,95 @@ out:
 }
 
 /**
+ * ufshcd_tune_pa_tactivate - Tunes PA_TActivate of local UniPro
+ * @hba: per-adapter instance
+ *
+ * PA_TActivate parameter can be tuned manually if UniPro version is less than
+ * 1.61. PA_TActivate needs to be greater than or equal to peerM-PHY's
+ * RX_MIN_ACTIVATETIME_CAPABILITY attribute. This optimal value can help reduce
+ * the hibern8 exit latency.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_tune_pa_tactivate(struct ufs_hba *hba)
+{
+   int ret = 0;
+   u32 peer_rx_min_activatetime = 0, tuned_pa_tactivate;
+
+   ret = ufshcd_dme_peer_get(hba,
+ UIC_ARG_MIB_SEL(
+   RX_MIN_ACTIVATETIME_CAPABILITY,
+   UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
+ _rx_min_activatetime);
+   if (ret)
+   goto out;
+
+   /* make sure proper unit conversion is applied */
+   tuned_pa_tactivate =
+   ((peer_rx_min_activatetime * RX_MIN_ACTIVATETIME_UNIT_US)
+/ PA_TACTIVATE_TIME_UNIT_US);
+   ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE),
+tuned_pa_tactivate);
+
+out:
+   return ret;
+}
+
+/**
+ * ufshcd_tune_pa_hibern8time - Tunes PA_Hibern8Time of local UniPro
+ * @hba: per-adapter instance
+ *
+ * PA_Hibern8Time parameter can be tuned manually if UniPro version is less 
than
+ * 1.61. PA_Hibern8Time needs to be maximum of local M-PHY's
+ * TX_HIBERN8TIME_CAPABILITY & peer M-PHY's RX_HIBERN8TIME_CAPABILITY.
+ * This optimal value can help reduce the hibern8 exit latency.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_tune_pa_hibern8time(struct ufs_hba *hba)
+{
+   int ret = 0;
+   u32 local_tx_hibern8_time_cap = 0, peer_rx_hibern8_time_cap = 0;
+   u32 max_hibern8_time, tuned_pa_hibern8time;
+
+   ret = ufshcd_dme_get(hba,
+UIC_ARG_MIB_SEL(TX_HIBERN8TIME_CAPABILITY,
+   UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
+ _tx_hibern8_time_cap);
+   if (ret)
+   goto out;
+
+   ret = ufshcd_dme_peer_get(hba,
+ UIC_ARG_MIB_SEL(RX_HIBERN8TIME_CAPABILITY,
+   UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
+ _rx_hibern8_time_cap);
+   if (ret)
+   goto out;
+
+   max_hibern8_time = max(local_tx_hibern8_time_cap,
+  peer_rx_hibern8_time_cap);
+   /* make sure proper unit 

[PATCH v7 08/17] scsi: ufs: make error handling bit faster

2016-03-08 Thread Yaniv Gardi
UFS driver's error handler forcefully tries to clear all the pending
requests. For each pending request in the queue, it waits 1 sec for it
to get cleared. If we have multiple requests in the queue then it's
possible that we might end up waiting for those many seconds before
resetting the host. But note that resetting host would any way clear
all the pending requests from the hardware. Hence this change skips
the forceful clear of the pending requests if we are anyway going to
reset the host (for fatal errors).

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 155 +-
 1 file changed, 112 insertions(+), 43 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 987cf27..dc096f1 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -133,9 +133,11 @@ enum {
 /* 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 */
+   UFSHCD_UIC_DL_NAC_RECEIVED_ERROR = (1 << 1), /* Data link layer error */
+   UFSHCD_UIC_DL_TCx_REPLAY_ERROR = (1 << 2), /* Data link layer error */
+   UFSHCD_UIC_NL_ERROR = (1 << 3), /* Network layer error */
+   UFSHCD_UIC_TL_ERROR = (1 << 4), /* Transport Layer error */
+   UFSHCD_UIC_DME_ERROR = (1 << 5), /* DME error */
 };
 
 /* Interrupt configuration options */
@@ -3465,31 +3467,18 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, 
u32 intr_status)
 }
 
 /**
- * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * __ufshcd_transfer_req_compl - handle SCSI and query command completion
  * @hba: per adapter instance
+ * @completed_reqs: requests to complete
  */
-static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
+   unsigned long completed_reqs)
 {
struct ufshcd_lrb *lrbp;
struct scsi_cmnd *cmd;
-   unsigned long completed_reqs;
-   u32 tr_doorbell;
int result;
int index;
 
-   /* Resetting interrupt aggregation counters first and reading the
-* DOOR_BELL afterward allows us to handle all the completed requests.
-* In order to prevent other interrupts starvation the DB is read once
-* after reset. The down side of this solution is the possibility of
-* false interrupt if device completes another request after resetting
-* aggregation and before reading the DB.
-*/
-   if (ufshcd_is_intr_aggr_allowed(hba))
-   ufshcd_reset_intr_aggr(hba);
-
-   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
-   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
-
for_each_set_bit(index, _reqs, hba->nutrs) {
lrbp = >lrb[index];
cmd = lrbp->cmd;
@@ -3519,6 +3508,31 @@ static void ufshcd_transfer_req_compl(struct ufs_hba 
*hba)
 }
 
 /**
+ * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+{
+   unsigned long completed_reqs;
+   u32 tr_doorbell;
+
+   /* Resetting interrupt aggregation counters first and reading the
+* DOOR_BELL afterward allows us to handle all the completed requests.
+* In order to prevent other interrupts starvation the DB is read once
+* after reset. The down side of this solution is the possibility of
+* false interrupt if device completes another request after resetting
+* aggregation and before reading the DB.
+*/
+   if (ufshcd_is_intr_aggr_allowed(hba))
+   ufshcd_reset_intr_aggr(hba);
+
+   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
+
+   __ufshcd_transfer_req_compl(hba, completed_reqs);
+}
+
+/**
  * ufshcd_disable_ee - disable exception event
  * @hba: per-adapter instance
  * @mask: exception event to disable
@@ -3773,6 +3787,13 @@ out:
return;
 }
 
+/* Complete requests that have door-bell cleared */
+static void ufshcd_complete_requests(struct ufs_hba *hba)
+{
+   ufshcd_transfer_req_compl(hba);
+   ufshcd_tmc_handler(hba);
+}
+
 /**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
@@ -3785,6 +3806,7 @@ static void ufshcd_err_handler(struct work_struct *work)
u32 err_tm = 0;
int err = 0;
int tag;
+   bool needs_reset = false;
 
hba = container_of(work, struct ufs_hba, eh_w

[PATCH v7 13/17] scsi: ufs: fix leakage during link off state

2016-03-08 Thread Yaniv Gardi
Currently when we try to put the link in off/disabled state during
suspend, it seems link is not being kept in low power mode.
This patch fixes the issue by putting the link in hibern8 first
(so device also puts the link in low power mode) and then stop the
host controller.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c9c4d68..f108a0b 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5619,6 +5619,16 @@ static int ufshcd_link_state_transition(struct ufs_hba 
*hba,
   (!check_for_bkops || (check_for_bkops &&
!hba->auto_bkops_enabled))) {
/*
+* Let's make sure that link is in low power mode, we are doing
+* this currently by putting the link in Hibern8. Otherway to
+* put the link in low power mode is to send the DME end point
+* to device and then send the DME reset command to local
+* unipro. But putting the link in hibern8 is much faster.
+*/
+   ret = ufshcd_uic_hibern8_enter(hba);
+   if (ret)
+   goto out;
+   /*
 * Change controller state to "reset state" which
 * should also put the link in off/reset state
 */
-- 
1.8.5.2

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


[PATCH v7 06/17] scsi: ufs: separate device and host quirks

2016-03-08 Thread Yaniv Gardi
Currently we use the host quirks mechanism in order to
handle both device and host controller quirks.
In order to support various of UFS devices we should separate
handling the device quirks from the host controller's.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Raviv Shvili <rshv...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/Makefile |   2 +-
 drivers/scsi/ufs/ufs.h|  31 +++
 drivers/scsi/ufs/ufs_quirks.c | 100 ++
 drivers/scsi/ufs/ufs_quirks.h | 124 ++
 drivers/scsi/ufs/ufshcd.c |   2 +
 drivers/scsi/ufs/ufshcd.h |   3 +
 6 files changed, 261 insertions(+), 1 deletion(-)
 create mode 100644 drivers/scsi/ufs/ufs_quirks.c
 create mode 100644 drivers/scsi/ufs/ufs_quirks.h

diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 8303bcc..8570d41 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -1,5 +1,5 @@
 # UFSHCD makefile
 obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
-obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
+obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o ufs_quirks.o
 obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
 obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index aacb235..8ec6356 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -196,6 +196,37 @@ enum unit_desc_param {
UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1  = 0x22,
 };
 
+/* Device descriptor parameters offsets in bytes*/
+enum device_desc_param {
+   DEVICE_DESC_PARAM_LEN   = 0x0,
+   DEVICE_DESC_PARAM_TYPE  = 0x1,
+   DEVICE_DESC_PARAM_DEVICE_TYPE   = 0x2,
+   DEVICE_DESC_PARAM_DEVICE_CLASS  = 0x3,
+   DEVICE_DESC_PARAM_DEVICE_SUB_CLASS  = 0x4,
+   DEVICE_DESC_PARAM_PRTCL = 0x5,
+   DEVICE_DESC_PARAM_NUM_LU= 0x6,
+   DEVICE_DESC_PARAM_NUM_WLU   = 0x7,
+   DEVICE_DESC_PARAM_BOOT_ENBL = 0x8,
+   DEVICE_DESC_PARAM_DESC_ACCSS_ENBL   = 0x9,
+   DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA,
+   DEVICE_DESC_PARAM_HIGH_PR_LUN   = 0xB,
+   DEVICE_DESC_PARAM_SEC_RMV_TYPE  = 0xC,
+   DEVICE_DESC_PARAM_SEC_LU= 0xD,
+   DEVICE_DESC_PARAM_BKOP_TERM_LT  = 0xE,
+   DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF,
+   DEVICE_DESC_PARAM_SPEC_VER  = 0x10,
+   DEVICE_DESC_PARAM_MANF_DATE = 0x12,
+   DEVICE_DESC_PARAM_MANF_NAME = 0x14,
+   DEVICE_DESC_PARAM_PRDCT_NAME= 0x15,
+   DEVICE_DESC_PARAM_SN= 0x16,
+   DEVICE_DESC_PARAM_OEM_ID= 0x17,
+   DEVICE_DESC_PARAM_MANF_ID   = 0x18,
+   DEVICE_DESC_PARAM_UD_OFFSET = 0x1A,
+   DEVICE_DESC_PARAM_UD_LEN= 0x1B,
+   DEVICE_DESC_PARAM_RTT_CAP   = 0x1C,
+   DEVICE_DESC_PARAM_FRQ_RTC   = 0x1D,
+};
+
 /*
  * Logical Unit Write Protect
  * 00h: LU not write protected
diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c
new file mode 100644
index 000..476ed01
--- /dev/null
+++ b/drivers/scsi/ufs/ufs_quirks.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ufshcd.h"
+#include "ufs_quirks.h"
+
+static struct ufs_dev_fix ufs_fixups[] = {
+   /* UFS cards deviations table */
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_NO_FASTAUTO),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
+   UFS_DEVICE_QUIRK_PA_TACTIVATE),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
+   UFS_DEVICE_QUIRK_PA_TACTIVATE),
+
+   END_FIX
+};
+
+static int ufs_get_device_info(struct ufs_hba *hba,
+   struct ufs_device_info *card_data)
+{
+   int err;
+   u8 model_index;
+   u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE + 1] = {0};
+   u8 desc_buf[QUERY_DESC_DEVICE_

[PATCH v7 05/17] scsi: ufs: add support to read device and string descriptors

2016-03-08 Thread Yaniv Gardi
This change adds support to read device descriptor and string descriptor
from a UFS device

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Reviewed-by: Hannes Reinecke <h...@suse.com>
Signed-off-by: Raviv Shvili <rshv...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs.h|  1 +
 drivers/scsi/ufs/ufshcd.c | 88 ++-
 drivers/scsi/ufs/ufshcd.h |  7 
 3 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 54a16ce..aacb235 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -43,6 +43,7 @@
 #define GENERAL_UPIU_REQUEST_SIZE 32
 #define QUERY_DESC_MAX_SIZE   255
 #define QUERY_DESC_MIN_SIZE   2
+#define QUERY_DESC_HDR_SIZE   2
 #define QUERY_OSF_SIZE(GENERAL_UPIU_REQUEST_SIZE - \
(sizeof(struct utp_upiu_header)))
 
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 80031e6..e2ed415 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -39,7 +39,7 @@
 
 #include 
 #include 
-
+#include 
 #include 
 #include "ufshcd.h"
 #include "unipro.h"
@@ -232,6 +232,16 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
}
 }
 
+/* replace non-printable or non-ASCII characters with spaces */
+static inline void ufshcd_remove_non_printable(char *val)
+{
+   if (!val)
+   return;
+
+   if (*val < 0x20 || *val > 0x7e)
+   *val = ' ';
+}
+
 /*
  * ufshcd_wait_for_register - wait for register value to change
  * @hba - per-adapter interface
@@ -2021,6 +2031,82 @@ static inline int ufshcd_read_power_desc(struct ufs_hba 
*hba,
return ufshcd_read_desc(hba, QUERY_DESC_IDN_POWER, 0, buf, size);
 }
 
+int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size)
+{
+   return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, buf, size);
+}
+EXPORT_SYMBOL(ufshcd_read_device_desc);
+
+/**
+ * ufshcd_read_string_desc - read string descriptor
+ * @hba: pointer to adapter instance
+ * @desc_index: descriptor index
+ * @buf: pointer to buffer where descriptor would be read
+ * @size: size of buf
+ * @ascii: if true convert from unicode to ascii characters
+ *
+ * Return 0 in case of success, non-zero otherwise
+ */
+int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
+   u32 size, bool ascii)
+{
+   int err = 0;
+
+   err = ufshcd_read_desc(hba,
+   QUERY_DESC_IDN_STRING, desc_index, buf, size);
+
+   if (err) {
+   dev_err(hba->dev, "%s: reading String Desc failed after %d 
retries. err = %d\n",
+   __func__, QUERY_REQ_RETRIES, err);
+   goto out;
+   }
+
+   if (ascii) {
+   int desc_len;
+   int ascii_len;
+   int i;
+   char *buff_ascii;
+
+   desc_len = buf[0];
+   /* remove header and divide by 2 to move from UTF16 to UTF8 */
+   ascii_len = (desc_len - QUERY_DESC_HDR_SIZE) / 2 + 1;
+   if (size < ascii_len + QUERY_DESC_HDR_SIZE) {
+   dev_err(hba->dev, "%s: buffer allocated size is too 
small\n",
+   __func__);
+   err = -ENOMEM;
+   goto out;
+   }
+
+   buff_ascii = kmalloc(ascii_len, GFP_KERNEL);
+   if (!buff_ascii) {
+   err = -ENOMEM;
+   goto out_free_buff;
+   }
+
+   /*
+* the descriptor contains string in UTF16 format
+* we need to convert to utf-8 so it can be displayed
+*/
+   utf16s_to_utf8s((wchar_t *)[QUERY_DESC_HDR_SIZE],
+   desc_len - QUERY_DESC_HDR_SIZE,
+   UTF16_BIG_ENDIAN, buff_ascii, ascii_len);
+
+   /* replace non-printable or non-ASCII characters with spaces */
+   for (i = 0; i < ascii_len; i++)
+   ufshcd_remove_non_printable(_ascii[i]);
+
+   memset(buf + QUERY_DESC_HDR_SIZE, 0,
+   size - QUERY_DESC_HDR_SIZE);
+   memcpy(buf + QUERY_DESC_HDR_SIZE, buff_ascii, ascii_len);
+   buf[QUERY_DESC_LENGTH_OFFSET] = ascii_len + QUERY_DESC_HDR_SIZE;
+out_free_buff:
+   kfree(buff_ascii);
+   }
+out:
+   return err;
+}
+EXPORT_SYMBOL(ufshcd_read_string_desc);
+
 /**
  * ufshcd_read_unit_desc_param - read the specified unit descriptor parameter
  * @hba: Pointer to adapter instance
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index a6d3572..54e13cc 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/

[PATCH v7 06/17] scsi: ufs: separate device and host quirks

2016-03-08 Thread Yaniv Gardi
Currently we use the host quirks mechanism in order to
handle both device and host controller quirks.
In order to support various of UFS devices we should separate
handling the device quirks from the host controller's.

Reviewed-by: Gilad Broner 
Reviewed-by: Hannes Reinecke 
Signed-off-by: Raviv Shvili 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/Makefile |   2 +-
 drivers/scsi/ufs/ufs.h|  31 +++
 drivers/scsi/ufs/ufs_quirks.c | 100 ++
 drivers/scsi/ufs/ufs_quirks.h | 124 ++
 drivers/scsi/ufs/ufshcd.c |   2 +
 drivers/scsi/ufs/ufshcd.h |   3 +
 6 files changed, 261 insertions(+), 1 deletion(-)
 create mode 100644 drivers/scsi/ufs/ufs_quirks.c
 create mode 100644 drivers/scsi/ufs/ufs_quirks.h

diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 8303bcc..8570d41 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -1,5 +1,5 @@
 # UFSHCD makefile
 obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
-obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
+obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o ufs_quirks.o
 obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
 obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index aacb235..8ec6356 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -196,6 +196,37 @@ enum unit_desc_param {
UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1  = 0x22,
 };
 
+/* Device descriptor parameters offsets in bytes*/
+enum device_desc_param {
+   DEVICE_DESC_PARAM_LEN   = 0x0,
+   DEVICE_DESC_PARAM_TYPE  = 0x1,
+   DEVICE_DESC_PARAM_DEVICE_TYPE   = 0x2,
+   DEVICE_DESC_PARAM_DEVICE_CLASS  = 0x3,
+   DEVICE_DESC_PARAM_DEVICE_SUB_CLASS  = 0x4,
+   DEVICE_DESC_PARAM_PRTCL = 0x5,
+   DEVICE_DESC_PARAM_NUM_LU= 0x6,
+   DEVICE_DESC_PARAM_NUM_WLU   = 0x7,
+   DEVICE_DESC_PARAM_BOOT_ENBL = 0x8,
+   DEVICE_DESC_PARAM_DESC_ACCSS_ENBL   = 0x9,
+   DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA,
+   DEVICE_DESC_PARAM_HIGH_PR_LUN   = 0xB,
+   DEVICE_DESC_PARAM_SEC_RMV_TYPE  = 0xC,
+   DEVICE_DESC_PARAM_SEC_LU= 0xD,
+   DEVICE_DESC_PARAM_BKOP_TERM_LT  = 0xE,
+   DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF,
+   DEVICE_DESC_PARAM_SPEC_VER  = 0x10,
+   DEVICE_DESC_PARAM_MANF_DATE = 0x12,
+   DEVICE_DESC_PARAM_MANF_NAME = 0x14,
+   DEVICE_DESC_PARAM_PRDCT_NAME= 0x15,
+   DEVICE_DESC_PARAM_SN= 0x16,
+   DEVICE_DESC_PARAM_OEM_ID= 0x17,
+   DEVICE_DESC_PARAM_MANF_ID   = 0x18,
+   DEVICE_DESC_PARAM_UD_OFFSET = 0x1A,
+   DEVICE_DESC_PARAM_UD_LEN= 0x1B,
+   DEVICE_DESC_PARAM_RTT_CAP   = 0x1C,
+   DEVICE_DESC_PARAM_FRQ_RTC   = 0x1D,
+};
+
 /*
  * Logical Unit Write Protect
  * 00h: LU not write protected
diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c
new file mode 100644
index 000..476ed01
--- /dev/null
+++ b/drivers/scsi/ufs/ufs_quirks.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ufshcd.h"
+#include "ufs_quirks.h"
+
+static struct ufs_dev_fix ufs_fixups[] = {
+   /* UFS cards deviations table */
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_NO_FASTAUTO),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
+   UFS_DEVICE_QUIRK_PA_TACTIVATE),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
+   UFS_DEVICE_QUIRK_PA_TACTIVATE),
+
+   END_FIX
+};
+
+static int ufs_get_device_info(struct ufs_hba *hba,
+   struct ufs_device_info *card_data)
+{
+   int err;
+   u8 model_index;
+   u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE + 1] = {0};
+   u8 desc_buf[QUERY_DESC_DEVICE_MAX_SIZE];
+
+   err = ufshcd_read_device_desc(hba, desc_buf,
+   QUERY_DESC_DEVICE_MAX_SIZE);
+   if (err) {
+

[PATCH v7 05/17] scsi: ufs: add support to read device and string descriptors

2016-03-08 Thread Yaniv Gardi
This change adds support to read device descriptor and string descriptor
from a UFS device

Reviewed-by: Gilad Broner 
Reviewed-by: Hannes Reinecke 
Signed-off-by: Raviv Shvili 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs.h|  1 +
 drivers/scsi/ufs/ufshcd.c | 88 ++-
 drivers/scsi/ufs/ufshcd.h |  7 
 3 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 54a16ce..aacb235 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -43,6 +43,7 @@
 #define GENERAL_UPIU_REQUEST_SIZE 32
 #define QUERY_DESC_MAX_SIZE   255
 #define QUERY_DESC_MIN_SIZE   2
+#define QUERY_DESC_HDR_SIZE   2
 #define QUERY_OSF_SIZE(GENERAL_UPIU_REQUEST_SIZE - \
(sizeof(struct utp_upiu_header)))
 
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 80031e6..e2ed415 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -39,7 +39,7 @@
 
 #include 
 #include 
-
+#include 
 #include 
 #include "ufshcd.h"
 #include "unipro.h"
@@ -232,6 +232,16 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
}
 }
 
+/* replace non-printable or non-ASCII characters with spaces */
+static inline void ufshcd_remove_non_printable(char *val)
+{
+   if (!val)
+   return;
+
+   if (*val < 0x20 || *val > 0x7e)
+   *val = ' ';
+}
+
 /*
  * ufshcd_wait_for_register - wait for register value to change
  * @hba - per-adapter interface
@@ -2021,6 +2031,82 @@ static inline int ufshcd_read_power_desc(struct ufs_hba 
*hba,
return ufshcd_read_desc(hba, QUERY_DESC_IDN_POWER, 0, buf, size);
 }
 
+int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size)
+{
+   return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, buf, size);
+}
+EXPORT_SYMBOL(ufshcd_read_device_desc);
+
+/**
+ * ufshcd_read_string_desc - read string descriptor
+ * @hba: pointer to adapter instance
+ * @desc_index: descriptor index
+ * @buf: pointer to buffer where descriptor would be read
+ * @size: size of buf
+ * @ascii: if true convert from unicode to ascii characters
+ *
+ * Return 0 in case of success, non-zero otherwise
+ */
+int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
+   u32 size, bool ascii)
+{
+   int err = 0;
+
+   err = ufshcd_read_desc(hba,
+   QUERY_DESC_IDN_STRING, desc_index, buf, size);
+
+   if (err) {
+   dev_err(hba->dev, "%s: reading String Desc failed after %d 
retries. err = %d\n",
+   __func__, QUERY_REQ_RETRIES, err);
+   goto out;
+   }
+
+   if (ascii) {
+   int desc_len;
+   int ascii_len;
+   int i;
+   char *buff_ascii;
+
+   desc_len = buf[0];
+   /* remove header and divide by 2 to move from UTF16 to UTF8 */
+   ascii_len = (desc_len - QUERY_DESC_HDR_SIZE) / 2 + 1;
+   if (size < ascii_len + QUERY_DESC_HDR_SIZE) {
+   dev_err(hba->dev, "%s: buffer allocated size is too 
small\n",
+   __func__);
+   err = -ENOMEM;
+   goto out;
+   }
+
+   buff_ascii = kmalloc(ascii_len, GFP_KERNEL);
+   if (!buff_ascii) {
+   err = -ENOMEM;
+   goto out_free_buff;
+   }
+
+   /*
+* the descriptor contains string in UTF16 format
+* we need to convert to utf-8 so it can be displayed
+*/
+   utf16s_to_utf8s((wchar_t *)[QUERY_DESC_HDR_SIZE],
+   desc_len - QUERY_DESC_HDR_SIZE,
+   UTF16_BIG_ENDIAN, buff_ascii, ascii_len);
+
+   /* replace non-printable or non-ASCII characters with spaces */
+   for (i = 0; i < ascii_len; i++)
+   ufshcd_remove_non_printable(_ascii[i]);
+
+   memset(buf + QUERY_DESC_HDR_SIZE, 0,
+   size - QUERY_DESC_HDR_SIZE);
+   memcpy(buf + QUERY_DESC_HDR_SIZE, buff_ascii, ascii_len);
+   buf[QUERY_DESC_LENGTH_OFFSET] = ascii_len + QUERY_DESC_HDR_SIZE;
+out_free_buff:
+   kfree(buff_ascii);
+   }
+out:
+   return err;
+}
+EXPORT_SYMBOL(ufshcd_read_string_desc);
+
 /**
  * ufshcd_read_unit_desc_param - read the specified unit descriptor parameter
  * @hba: Pointer to adapter instance
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index a6d3572..54e13cc 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -678,6 +678,13 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
return ufshcd

[PATCH v7 17/17] scsi: ufs-qcom: add printouts of testbus debug registers

2016-03-08 Thread Yaniv Gardi
This change adds printouts of testbus and debug registers.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs-qcom.c | 77 +
 drivers/scsi/ufs/ufs-qcom.h |  9 ++
 2 files changed, 86 insertions(+)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 849fcf3..3aedf73 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -58,6 +58,12 @@ static void ufs_qcom_dump_regs(struct ufs_hba *hba, int 
offset, int len,
len * 4, false);
 }
 
+static void ufs_qcom_dump_regs_wrapper(struct ufs_hba *hba, int offset, int 
len,
+   char *prefix, void *priv)
+{
+   ufs_qcom_dump_regs(hba, offset, len, prefix);
+}
+
 static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes)
 {
int err = 0;
@@ -1397,6 +1403,74 @@ out:
return err;
 }
 
+static void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba,
+   void *priv, void (*print_fn)(struct ufs_hba *hba,
+   int offset, int num_regs, char *str, void *priv))
+{
+   u32 reg;
+   struct ufs_qcom_host *host;
+
+   if (unlikely(!hba)) {
+   pr_err("%s: hba is NULL\n", __func__);
+   return;
+   }
+   if (unlikely(!print_fn)) {
+   dev_err(hba->dev, "%s: print_fn is NULL\n", __func__);
+   return;
+   }
+
+   host = ufshcd_get_variant(hba);
+   if (!(host->dbg_print_en & UFS_QCOM_DBG_PRINT_REGS_EN))
+   return;
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_REG_OCSC);
+   print_fn(hba, reg, 44, "UFS_UFS_DBG_RD_REG_OCSC ", priv);
+
+   reg = ufshcd_readl(hba, REG_UFS_CFG1);
+   reg |= UFS_BIT(17);
+   ufshcd_writel(hba, reg, REG_UFS_CFG1);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_EDTL_RAM);
+   print_fn(hba, reg, 32, "UFS_UFS_DBG_RD_EDTL_RAM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_DESC_RAM);
+   print_fn(hba, reg, 128, "UFS_UFS_DBG_RD_DESC_RAM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_PRDT_RAM);
+   print_fn(hba, reg, 64, "UFS_UFS_DBG_RD_PRDT_RAM ", priv);
+
+   ufshcd_writel(hba, (reg & ~UFS_BIT(17)), REG_UFS_CFG1);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UAWM);
+   print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UAWM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UARM);
+   print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UARM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TXUC);
+   print_fn(hba, reg, 48, "UFS_DBG_RD_REG_TXUC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_RXUC);
+   print_fn(hba, reg, 27, "UFS_DBG_RD_REG_RXUC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_DFC);
+   print_fn(hba, reg, 19, "UFS_DBG_RD_REG_DFC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TRLUT);
+   print_fn(hba, reg, 34, "UFS_DBG_RD_REG_TRLUT ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TMRLUT);
+   print_fn(hba, reg, 9, "UFS_DBG_RD_REG_TMRLUT ", priv);
+}
+
+static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host)
+{
+   if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
+   ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1);
+   else
+   ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1);
+}
+
 static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host)
 {
/* provide a legal default configuration */
@@ -1505,6 +1579,7 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
ufshcd_rmwl(host->hba, mask,
(u32)host->testbus.select_minor << offset,
reg);
+   ufs_qcom_enable_test_bus(host);
ufshcd_release(host->hba);
pm_runtime_put_sync(host->hba->dev);
 
@@ -1521,8 +1596,10 @@ static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16,
"HCI Vendor Specific Registers ");
 
+   ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper);
ufs_qcom_testbus_read(hba);
 }
+
 /**
  * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
  *
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 36249b3..a19307a 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -241,6 +241,15 @@ struct ufs_qcom_host {
struct ufs_qcom_testbus testbus;
 };
 
+static inline u32
+ufs_qcom_get_debug_

[PATCH v7 15/17] scsi: ufs-qcom: set PA_Local_TX_LCC_Enable before link startup

2016-03-08 Thread Yaniv Gardi
Some UFS devices (and may be host) have issues if LCC is
enabled. So we are setting PA_Local_TX_LCC_Enable to 0
before link startup which will make sure that both host
and device TX LCC are disabled once link startup is
completed.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs-qcom.c | 27 ---
 drivers/scsi/ufs/unipro.h   |  1 +
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index ed57729..966bacf 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -16,8 +16,8 @@
 #include 
 #include 
 #include 
-
 #include 
+
 #include "ufshcd.h"
 #include "ufshcd-pltfrm.h"
 #include "unipro.h"
@@ -106,9 +106,11 @@ static void ufs_qcom_disable_lane_clks(struct 
ufs_qcom_host *host)
if (!host->is_lane_clks_enabled)
return;
 
-   clk_disable_unprepare(host->tx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->tx_l1_sync_clk);
clk_disable_unprepare(host->tx_l0_sync_clk);
-   clk_disable_unprepare(host->rx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->rx_l1_sync_clk);
clk_disable_unprepare(host->rx_l0_sync_clk);
 
host->is_lane_clks_enabled = false;
@@ -272,9 +274,8 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B);
 
if (ret) {
-   dev_err(hba->dev,
-   "%s: ufs_qcom_phy_calibrate_phy()failed, ret = %d\n",
-   __func__, ret);
+   dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret 
= %d\n",
+   __func__, ret);
goto out;
}
 
@@ -524,6 +525,18 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba 
*hba,
err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba,
  150);
 
+   /*
+* Some UFS devices (and may be host) have issues if LCC is
+* enabled. So we are setting PA_Local_TX_LCC_Enable to 0
+* before link startup which will make sure that both host
+* and device TX LCC are disabled once link startup is
+* completed.
+*/
+   if (ufshcd_get_local_unipro_ver(hba) != UFS_UNIPRO_VER_1_41)
+   err = ufshcd_dme_set(hba,
+   UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE),
+   0);
+
break;
case POST_CHANGE:
ufs_qcom_link_startup_post_change(hba);
@@ -1542,7 +1555,7 @@ static int ufs_qcom_probe(struct platform_device *pdev)
  * ufs_qcom_remove - set driver_data of the device to NULL
  * @pdev: pointer to platform device handle
  *
- * Always return 0
+ * Always returns 0
  */
 static int ufs_qcom_remove(struct platform_device *pdev)
 {
diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h
index 8082020..e2854e4 100644
--- a/drivers/scsi/ufs/unipro.h
+++ b/drivers/scsi/ufs/unipro.h
@@ -75,6 +75,7 @@
 #define PA_MAXRXSPEEDFAST  0x1541
 #define PA_MAXRXSPEEDSLOW  0x1542
 #define PA_TXLINKSTARTUPHS 0x1544
+#define PA_LOCAL_TX_LCC_ENABLE 0x155E
 #define PA_TXSPEEDFAST 0x1565
 #define PA_TXSPEEDSLOW 0x1566
 #define PA_REMOTEVERINFO   0x15A0
-- 
1.8.5.2

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


[PATCH v7 10/17] scsi: ufs: add retry for query descriptors

2016-03-08 Thread Yaniv Gardi
Query commands have 100ms timeout and it may timeout if they are
issued in parallel to ongoing read/write SCSI commands, this change
adds the retry (max: 10) in case command timeouts.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 55 +++
 1 file changed, 37 insertions(+), 18 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index e9c7c91..394054e 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1875,21 +1875,7 @@ static int ufshcd_query_attr_retry(struct ufs_hba *hba,
return ret;
 }
 
-/**
- * ufshcd_query_descriptor - API function for sending descriptor requests
- * hba: per-adapter instance
- * opcode: attribute opcode
- * idn: attribute idn to access
- * index: index field
- * selector: selector field
- * desc_buf: the buffer that contains the descriptor
- * buf_len: length parameter passed to the device
- *
- * Returns 0 for success, non-zero in case of failure.
- * The buf_len parameter will contain, on return, the length parameter
- * received on the response.
- */
-static int ufshcd_query_descriptor(struct ufs_hba *hba,
+static int __ufshcd_query_descriptor(struct ufs_hba *hba,
enum query_opcode opcode, enum desc_idn idn, u8 index,
u8 selector, u8 *desc_buf, int *buf_len)
 {
@@ -1954,6 +1940,39 @@ out:
 }
 
 /**
+ * ufshcd_query_descriptor_retry - API function for sending descriptor
+ * requests
+ * hba: per-adapter instance
+ * opcode: attribute opcode
+ * idn: attribute idn to access
+ * index: index field
+ * selector: selector field
+ * desc_buf: the buffer that contains the descriptor
+ * buf_len: length parameter passed to the device
+ *
+ * Returns 0 for success, non-zero in case of failure.
+ * The buf_len parameter will contain, on return, the length parameter
+ * received on the response.
+ */
+int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
+   enum query_opcode opcode, enum desc_idn idn, u8 index,
+   u8 selector, u8 *desc_buf, int *buf_len)
+{
+   int err;
+   int retries;
+
+   for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
+   err = __ufshcd_query_descriptor(hba, opcode, idn, index,
+   selector, desc_buf, buf_len);
+   if (!err || err == -EINVAL)
+   break;
+   }
+
+   return err;
+}
+EXPORT_SYMBOL(ufshcd_query_descriptor_retry);
+
+/**
  * ufshcd_read_desc_param - read the specified descriptor parameter
  * @hba: Pointer to adapter instance
  * @desc_id: descriptor idn value
@@ -1995,9 +2014,9 @@ static int ufshcd_read_desc_param(struct ufs_hba *hba,
return -ENOMEM;
}
 
-   ret = ufshcd_query_descriptor(hba, UPIU_QUERY_OPCODE_READ_DESC,
- desc_id, desc_index, 0, desc_buf,
- _len);
+   ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC,
+   desc_id, desc_index, 0, desc_buf,
+   _len);
 
if (ret || (buff_len < ufs_query_desc_max_size[desc_id]) ||
(desc_buf[QUERY_DESC_LENGTH_OFFSET] !=
-- 
1.8.5.2

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


[PATCH v7 14/17] scsi: ufs: add device quirk delay before putting UFS rails in LPM

2016-03-08 Thread Yaniv Gardi
We put the UFS device in sleep state & UFS link in hibern8 state during
runtime suspaned. After this we put all the UFS rails in low power
modes immediately but it seems some devices may still draw more than
sleep current from UFS rails (especially from VCCQ rail) atleast for
500us.
To avoid this situation, this change adds 2ms delay before putting
these UFS rails in LPM mode.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs_quirks.c |  4 
 drivers/scsi/ufs/ufs_quirks.h | 11 ++-
 drivers/scsi/ufs/ufshcd.c | 10 ++
 3 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c
index 476ed01..af68007 100644
--- a/drivers/scsi/ufs/ufs_quirks.c
+++ b/drivers/scsi/ufs/ufs_quirks.c
@@ -16,11 +16,15 @@
 
 static struct ufs_dev_fix ufs_fixups[] = {
/* UFS cards deviations table */
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_NO_FASTAUTO),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
UFS_DEVICE_QUIRK_PA_TACTIVATE),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h
index 01ab166..ffed0da 100644
--- a/drivers/scsi/ufs/ufs_quirks.h
+++ b/drivers/scsi/ufs/ufs_quirks.h
@@ -18,7 +18,7 @@
 /* return true if s1 is a prefix of s2 */
 #define STR_PRFX_EQUAL(s1, s2) !strncmp(s1, s2, strlen(s1))
 
-#define UFS_ANY_VENDOR -1
+#define UFS_ANY_VENDOR 0x
 #define UFS_ANY_MODEL  "ANY_MODEL"
 
 #define MAX_MODEL_LEN 16
@@ -119,6 +119,15 @@ struct ufs_dev_fix {
  */
 #define UFS_DEVICE_NO_FASTAUTO (1 << 5)
 
+/*
+ * It seems some UFS devices may keep drawing more than sleep current
+ * (atleast for 500us) from UFS rails (especially from VCCQ rail).
+ * To avoid this situation, add 2ms delay before putting these UFS
+ * rails in LPM mode.
+ */
+#define UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM  (1 << 6)
+
+
 struct ufs_hba;
 void ufs_advertise_fixup_device(struct ufs_hba *hba);
 #endif /* UFS_QUIRKS_H_ */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index f108a0b..da882cf 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5647,6 +5647,16 @@ out:
 static void ufshcd_vreg_set_lpm(struct ufs_hba *hba)
 {
/*
+* It seems some UFS devices may keep drawing more than sleep current
+* (atleast for 500us) from UFS rails (especially from VCCQ rail).
+* To avoid this situation, add 2ms delay before putting these UFS
+* rails in LPM mode.
+*/
+   if (!ufshcd_is_link_active(hba) &&
+   hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM)
+   usleep_range(2000, 2100);
+
+   /*
 * If UFS device is either in UFS_Sleep turn off VCC rail to save some
 * power.
 *
-- 
1.8.5.2

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


[PATCH v7 16/17] scsi: ufs-qcom: enable/disable the device ref clock

2016-03-08 Thread Yaniv Gardi
This change enables the device ref clock before changing to HS mode
and disables it if entered to PWM mode.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs-qcom.c | 12 
 drivers/scsi/ufs/ufshcd.h   |  9 +
 2 files changed, 21 insertions(+)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 966bacf..849fcf3 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -980,6 +980,10 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
goto out;
}
 
+   /* enable the device ref clock before changing to HS mode */
+   if (!ufshcd_is_hs_mode(>pwr_info) &&
+   ufshcd_is_hs_mode(dev_req_params))
+   ufs_qcom_dev_ref_clk_ctrl(host, true);
break;
case POST_CHANGE:
if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx,
@@ -1007,6 +1011,11 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba 
*hba,
memcpy(>dev_req_params,
dev_req_params, sizeof(*dev_req_params));
ufs_qcom_update_bus_bw_vote(host);
+
+   /* disable the device ref clock if entered PWM mode */
+   if (ufshcd_is_hs_mode(>pwr_info) &&
+   !ufshcd_is_hs_mode(dev_req_params))
+   ufs_qcom_dev_ref_clk_ctrl(host, false);
break;
default:
ret = -EINVAL;
@@ -1108,6 +1117,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, 
bool on)
ufs_qcom_phy_disable_iface_clk(host->generic_phy);
goto out;
}
+   /* enable the device ref clock for HS mode*/
+   if (ufshcd_is_hs_mode(>pwr_info))
+   ufs_qcom_dev_ref_clk_ctrl(host, true);
vote = host->bus_vote.saved_vote;
if (vote == host->bus_vote.min_bw_vote)
ufs_qcom_update_bus_bw_vote(host);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 41d9bfd..4bb6566 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -54,6 +54,7 @@
 #include 
 #include 
 #include 
+#include "unipro.h"
 
 #include 
 #include 
@@ -689,6 +690,14 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
 
 int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size);
 
+static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info)
+{
+   return (pwr_info->pwr_rx == FAST_MODE ||
+   pwr_info->pwr_rx == FASTAUTO_MODE) &&
+   (pwr_info->pwr_tx == FAST_MODE ||
+   pwr_info->pwr_tx == FASTAUTO_MODE);
+}
+
 #define ASCII_STD true
 
 int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
-- 
1.8.5.2

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


[PATCH v7 17/17] scsi: ufs-qcom: add printouts of testbus debug registers

2016-03-08 Thread Yaniv Gardi
This change adds printouts of testbus and debug registers.

Reviewed-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs-qcom.c | 77 +
 drivers/scsi/ufs/ufs-qcom.h |  9 ++
 2 files changed, 86 insertions(+)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 849fcf3..3aedf73 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -58,6 +58,12 @@ static void ufs_qcom_dump_regs(struct ufs_hba *hba, int 
offset, int len,
len * 4, false);
 }
 
+static void ufs_qcom_dump_regs_wrapper(struct ufs_hba *hba, int offset, int 
len,
+   char *prefix, void *priv)
+{
+   ufs_qcom_dump_regs(hba, offset, len, prefix);
+}
+
 static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes)
 {
int err = 0;
@@ -1397,6 +1403,74 @@ out:
return err;
 }
 
+static void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba,
+   void *priv, void (*print_fn)(struct ufs_hba *hba,
+   int offset, int num_regs, char *str, void *priv))
+{
+   u32 reg;
+   struct ufs_qcom_host *host;
+
+   if (unlikely(!hba)) {
+   pr_err("%s: hba is NULL\n", __func__);
+   return;
+   }
+   if (unlikely(!print_fn)) {
+   dev_err(hba->dev, "%s: print_fn is NULL\n", __func__);
+   return;
+   }
+
+   host = ufshcd_get_variant(hba);
+   if (!(host->dbg_print_en & UFS_QCOM_DBG_PRINT_REGS_EN))
+   return;
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_REG_OCSC);
+   print_fn(hba, reg, 44, "UFS_UFS_DBG_RD_REG_OCSC ", priv);
+
+   reg = ufshcd_readl(hba, REG_UFS_CFG1);
+   reg |= UFS_BIT(17);
+   ufshcd_writel(hba, reg, REG_UFS_CFG1);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_EDTL_RAM);
+   print_fn(hba, reg, 32, "UFS_UFS_DBG_RD_EDTL_RAM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_DESC_RAM);
+   print_fn(hba, reg, 128, "UFS_UFS_DBG_RD_DESC_RAM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_PRDT_RAM);
+   print_fn(hba, reg, 64, "UFS_UFS_DBG_RD_PRDT_RAM ", priv);
+
+   ufshcd_writel(hba, (reg & ~UFS_BIT(17)), REG_UFS_CFG1);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UAWM);
+   print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UAWM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UARM);
+   print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UARM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TXUC);
+   print_fn(hba, reg, 48, "UFS_DBG_RD_REG_TXUC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_RXUC);
+   print_fn(hba, reg, 27, "UFS_DBG_RD_REG_RXUC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_DFC);
+   print_fn(hba, reg, 19, "UFS_DBG_RD_REG_DFC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TRLUT);
+   print_fn(hba, reg, 34, "UFS_DBG_RD_REG_TRLUT ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TMRLUT);
+   print_fn(hba, reg, 9, "UFS_DBG_RD_REG_TMRLUT ", priv);
+}
+
+static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host)
+{
+   if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
+   ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1);
+   else
+   ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1);
+}
+
 static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host)
 {
/* provide a legal default configuration */
@@ -1505,6 +1579,7 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
ufshcd_rmwl(host->hba, mask,
(u32)host->testbus.select_minor << offset,
reg);
+   ufs_qcom_enable_test_bus(host);
ufshcd_release(host->hba);
pm_runtime_put_sync(host->hba->dev);
 
@@ -1521,8 +1596,10 @@ static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16,
"HCI Vendor Specific Registers ");
 
+   ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper);
ufs_qcom_testbus_read(hba);
 }
+
 /**
  * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
  *
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 36249b3..a19307a 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -241,6 +241,15 @@ struct ufs_qcom_host {
struct ufs_qcom_testbus testbus;
 };
 
+static inline u32
+ufs_qcom_get_debug_reg_offset(struct ufs_qcom_host *host, u32 reg)
+{
+  

[PATCH v7 15/17] scsi: ufs-qcom: set PA_Local_TX_LCC_Enable before link startup

2016-03-08 Thread Yaniv Gardi
Some UFS devices (and may be host) have issues if LCC is
enabled. So we are setting PA_Local_TX_LCC_Enable to 0
before link startup which will make sure that both host
and device TX LCC are disabled once link startup is
completed.

Reviewed-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs-qcom.c | 27 ---
 drivers/scsi/ufs/unipro.h   |  1 +
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index ed57729..966bacf 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -16,8 +16,8 @@
 #include 
 #include 
 #include 
-
 #include 
+
 #include "ufshcd.h"
 #include "ufshcd-pltfrm.h"
 #include "unipro.h"
@@ -106,9 +106,11 @@ static void ufs_qcom_disable_lane_clks(struct 
ufs_qcom_host *host)
if (!host->is_lane_clks_enabled)
return;
 
-   clk_disable_unprepare(host->tx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->tx_l1_sync_clk);
clk_disable_unprepare(host->tx_l0_sync_clk);
-   clk_disable_unprepare(host->rx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->rx_l1_sync_clk);
clk_disable_unprepare(host->rx_l0_sync_clk);
 
host->is_lane_clks_enabled = false;
@@ -272,9 +274,8 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B);
 
if (ret) {
-   dev_err(hba->dev,
-   "%s: ufs_qcom_phy_calibrate_phy()failed, ret = %d\n",
-   __func__, ret);
+   dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret 
= %d\n",
+   __func__, ret);
goto out;
}
 
@@ -524,6 +525,18 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba 
*hba,
err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba,
  150);
 
+   /*
+* Some UFS devices (and may be host) have issues if LCC is
+* enabled. So we are setting PA_Local_TX_LCC_Enable to 0
+* before link startup which will make sure that both host
+* and device TX LCC are disabled once link startup is
+* completed.
+*/
+   if (ufshcd_get_local_unipro_ver(hba) != UFS_UNIPRO_VER_1_41)
+   err = ufshcd_dme_set(hba,
+   UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE),
+   0);
+
break;
case POST_CHANGE:
ufs_qcom_link_startup_post_change(hba);
@@ -1542,7 +1555,7 @@ static int ufs_qcom_probe(struct platform_device *pdev)
  * ufs_qcom_remove - set driver_data of the device to NULL
  * @pdev: pointer to platform device handle
  *
- * Always return 0
+ * Always returns 0
  */
 static int ufs_qcom_remove(struct platform_device *pdev)
 {
diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h
index 8082020..e2854e4 100644
--- a/drivers/scsi/ufs/unipro.h
+++ b/drivers/scsi/ufs/unipro.h
@@ -75,6 +75,7 @@
 #define PA_MAXRXSPEEDFAST  0x1541
 #define PA_MAXRXSPEEDSLOW  0x1542
 #define PA_TXLINKSTARTUPHS 0x1544
+#define PA_LOCAL_TX_LCC_ENABLE 0x155E
 #define PA_TXSPEEDFAST 0x1565
 #define PA_TXSPEEDSLOW 0x1566
 #define PA_REMOTEVERINFO   0x15A0
-- 
1.8.5.2

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


[PATCH v7 10/17] scsi: ufs: add retry for query descriptors

2016-03-08 Thread Yaniv Gardi
Query commands have 100ms timeout and it may timeout if they are
issued in parallel to ongoing read/write SCSI commands, this change
adds the retry (max: 10) in case command timeouts.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 55 +++
 1 file changed, 37 insertions(+), 18 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index e9c7c91..394054e 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1875,21 +1875,7 @@ static int ufshcd_query_attr_retry(struct ufs_hba *hba,
return ret;
 }
 
-/**
- * ufshcd_query_descriptor - API function for sending descriptor requests
- * hba: per-adapter instance
- * opcode: attribute opcode
- * idn: attribute idn to access
- * index: index field
- * selector: selector field
- * desc_buf: the buffer that contains the descriptor
- * buf_len: length parameter passed to the device
- *
- * Returns 0 for success, non-zero in case of failure.
- * The buf_len parameter will contain, on return, the length parameter
- * received on the response.
- */
-static int ufshcd_query_descriptor(struct ufs_hba *hba,
+static int __ufshcd_query_descriptor(struct ufs_hba *hba,
enum query_opcode opcode, enum desc_idn idn, u8 index,
u8 selector, u8 *desc_buf, int *buf_len)
 {
@@ -1954,6 +1940,39 @@ out:
 }
 
 /**
+ * ufshcd_query_descriptor_retry - API function for sending descriptor
+ * requests
+ * hba: per-adapter instance
+ * opcode: attribute opcode
+ * idn: attribute idn to access
+ * index: index field
+ * selector: selector field
+ * desc_buf: the buffer that contains the descriptor
+ * buf_len: length parameter passed to the device
+ *
+ * Returns 0 for success, non-zero in case of failure.
+ * The buf_len parameter will contain, on return, the length parameter
+ * received on the response.
+ */
+int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
+   enum query_opcode opcode, enum desc_idn idn, u8 index,
+   u8 selector, u8 *desc_buf, int *buf_len)
+{
+   int err;
+   int retries;
+
+   for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
+   err = __ufshcd_query_descriptor(hba, opcode, idn, index,
+   selector, desc_buf, buf_len);
+   if (!err || err == -EINVAL)
+   break;
+   }
+
+   return err;
+}
+EXPORT_SYMBOL(ufshcd_query_descriptor_retry);
+
+/**
  * ufshcd_read_desc_param - read the specified descriptor parameter
  * @hba: Pointer to adapter instance
  * @desc_id: descriptor idn value
@@ -1995,9 +2014,9 @@ static int ufshcd_read_desc_param(struct ufs_hba *hba,
return -ENOMEM;
}
 
-   ret = ufshcd_query_descriptor(hba, UPIU_QUERY_OPCODE_READ_DESC,
- desc_id, desc_index, 0, desc_buf,
- _len);
+   ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC,
+   desc_id, desc_index, 0, desc_buf,
+   _len);
 
if (ret || (buff_len < ufs_query_desc_max_size[desc_id]) ||
(desc_buf[QUERY_DESC_LENGTH_OFFSET] !=
-- 
1.8.5.2

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


[PATCH v7 14/17] scsi: ufs: add device quirk delay before putting UFS rails in LPM

2016-03-08 Thread Yaniv Gardi
We put the UFS device in sleep state & UFS link in hibern8 state during
runtime suspaned. After this we put all the UFS rails in low power
modes immediately but it seems some devices may still draw more than
sleep current from UFS rails (especially from VCCQ rail) atleast for
500us.
To avoid this situation, this change adds 2ms delay before putting
these UFS rails in LPM mode.

Reviewed-by: Gilad Broner 
Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs_quirks.c |  4 
 drivers/scsi/ufs/ufs_quirks.h | 11 ++-
 drivers/scsi/ufs/ufshcd.c | 10 ++
 3 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c
index 476ed01..af68007 100644
--- a/drivers/scsi/ufs/ufs_quirks.c
+++ b/drivers/scsi/ufs/ufs_quirks.c
@@ -16,11 +16,15 @@
 
 static struct ufs_dev_fix ufs_fixups[] = {
/* UFS cards deviations table */
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_NO_FASTAUTO),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
UFS_DEVICE_QUIRK_PA_TACTIVATE),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h
index 01ab166..ffed0da 100644
--- a/drivers/scsi/ufs/ufs_quirks.h
+++ b/drivers/scsi/ufs/ufs_quirks.h
@@ -18,7 +18,7 @@
 /* return true if s1 is a prefix of s2 */
 #define STR_PRFX_EQUAL(s1, s2) !strncmp(s1, s2, strlen(s1))
 
-#define UFS_ANY_VENDOR -1
+#define UFS_ANY_VENDOR 0x
 #define UFS_ANY_MODEL  "ANY_MODEL"
 
 #define MAX_MODEL_LEN 16
@@ -119,6 +119,15 @@ struct ufs_dev_fix {
  */
 #define UFS_DEVICE_NO_FASTAUTO (1 << 5)
 
+/*
+ * It seems some UFS devices may keep drawing more than sleep current
+ * (atleast for 500us) from UFS rails (especially from VCCQ rail).
+ * To avoid this situation, add 2ms delay before putting these UFS
+ * rails in LPM mode.
+ */
+#define UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM  (1 << 6)
+
+
 struct ufs_hba;
 void ufs_advertise_fixup_device(struct ufs_hba *hba);
 #endif /* UFS_QUIRKS_H_ */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index f108a0b..da882cf 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5647,6 +5647,16 @@ out:
 static void ufshcd_vreg_set_lpm(struct ufs_hba *hba)
 {
/*
+* It seems some UFS devices may keep drawing more than sleep current
+* (atleast for 500us) from UFS rails (especially from VCCQ rail).
+* To avoid this situation, add 2ms delay before putting these UFS
+* rails in LPM mode.
+*/
+   if (!ufshcd_is_link_active(hba) &&
+   hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM)
+   usleep_range(2000, 2100);
+
+   /*
 * If UFS device is either in UFS_Sleep turn off VCC rail to save some
 * power.
 *
-- 
1.8.5.2

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


[PATCH v7 16/17] scsi: ufs-qcom: enable/disable the device ref clock

2016-03-08 Thread Yaniv Gardi
This change enables the device ref clock before changing to HS mode
and disables it if entered to PWM mode.

Reviewed-by: Gilad Broner 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs-qcom.c | 12 
 drivers/scsi/ufs/ufshcd.h   |  9 +
 2 files changed, 21 insertions(+)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 966bacf..849fcf3 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -980,6 +980,10 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
goto out;
}
 
+   /* enable the device ref clock before changing to HS mode */
+   if (!ufshcd_is_hs_mode(>pwr_info) &&
+   ufshcd_is_hs_mode(dev_req_params))
+   ufs_qcom_dev_ref_clk_ctrl(host, true);
break;
case POST_CHANGE:
if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx,
@@ -1007,6 +1011,11 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba 
*hba,
memcpy(>dev_req_params,
dev_req_params, sizeof(*dev_req_params));
ufs_qcom_update_bus_bw_vote(host);
+
+   /* disable the device ref clock if entered PWM mode */
+   if (ufshcd_is_hs_mode(>pwr_info) &&
+   !ufshcd_is_hs_mode(dev_req_params))
+   ufs_qcom_dev_ref_clk_ctrl(host, false);
break;
default:
ret = -EINVAL;
@@ -1108,6 +1117,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, 
bool on)
ufs_qcom_phy_disable_iface_clk(host->generic_phy);
goto out;
}
+   /* enable the device ref clock for HS mode*/
+   if (ufshcd_is_hs_mode(>pwr_info))
+   ufs_qcom_dev_ref_clk_ctrl(host, true);
vote = host->bus_vote.saved_vote;
if (vote == host->bus_vote.min_bw_vote)
ufs_qcom_update_bus_bw_vote(host);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 41d9bfd..4bb6566 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -54,6 +54,7 @@
 #include 
 #include 
 #include 
+#include "unipro.h"
 
 #include 
 #include 
@@ -689,6 +690,14 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
 
 int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size);
 
+static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info)
+{
+   return (pwr_info->pwr_rx == FAST_MODE ||
+   pwr_info->pwr_rx == FASTAUTO_MODE) &&
+   (pwr_info->pwr_tx == FAST_MODE ||
+   pwr_info->pwr_tx == FASTAUTO_MODE);
+}
+
 #define ASCII_STD true
 
 int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
-- 
1.8.5.2

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


[PATCH v7 11/17] scsi: ufs: handle non spec compliant bkops behaviour by device

2016-03-08 Thread Yaniv Gardi
We are seeing that some devices are raising the urgent bkops exception
events even when BKOPS status doesn't indicate performace impacted or
critical. Handle these device by determining their urgent bkops status
at runtime.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h |  6 ++
 2 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 394054e..d28c629 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3763,7 +3763,7 @@ out:
  */
 static int ufshcd_urgent_bkops(struct ufs_hba *hba)
 {
-   return ufshcd_bkops_ctrl(hba, BKOPS_STATUS_PERF_IMPACT);
+   return ufshcd_bkops_ctrl(hba, hba->urgent_bkops_lvl);
 }
 
 static inline int ufshcd_get_ee_status(struct ufs_hba *hba, u32 *status)
@@ -3772,6 +3772,43 @@ static inline int ufshcd_get_ee_status(struct ufs_hba 
*hba, u32 *status)
QUERY_ATTR_IDN_EE_STATUS, 0, 0, status);
 }
 
+static void ufshcd_bkops_exception_event_handler(struct ufs_hba *hba)
+{
+   int err;
+   u32 curr_status = 0;
+
+   if (hba->is_urgent_bkops_lvl_checked)
+   goto enable_auto_bkops;
+
+   err = ufshcd_get_bkops_status(hba, _status);
+   if (err) {
+   dev_err(hba->dev, "%s: failed to get BKOPS status %d\n",
+   __func__, err);
+   goto out;
+   }
+
+   /*
+* We are seeing that some devices are raising the urgent bkops
+* exception events even when BKOPS status doesn't indicate performace
+* impacted or critical. Handle these device by determining their urgent
+* bkops status at runtime.
+*/
+   if (curr_status < BKOPS_STATUS_PERF_IMPACT) {
+   dev_err(hba->dev, "%s: device raised urgent BKOPS exception for 
bkops status %d\n",
+   __func__, curr_status);
+   /* update the current status as the urgent bkops level */
+   hba->urgent_bkops_lvl = curr_status;
+   hba->is_urgent_bkops_lvl_checked = true;
+   }
+
+enable_auto_bkops:
+   err = ufshcd_enable_auto_bkops(hba);
+out:
+   if (err < 0)
+   dev_err(hba->dev, "%s: failed to handle urgent bkops %d\n",
+   __func__, err);
+}
+
 /**
  * ufshcd_exception_event_handler - handle exceptions raised by device
  * @work: pointer to work data
@@ -3795,12 +3832,10 @@ static void ufshcd_exception_event_handler(struct 
work_struct *work)
}
 
status &= hba->ee_ctrl_mask;
-   if (status & MASK_EE_URGENT_BKOPS) {
-   err = ufshcd_urgent_bkops(hba);
-   if (err < 0)
-   dev_err(hba->dev, "%s: failed to handle urgent bkops 
%d\n",
-   __func__, err);
-   }
+
+   if (status & MASK_EE_URGENT_BKOPS)
+   ufshcd_bkops_exception_event_handler(hba);
+
 out:
pm_runtime_put_sync(hba->dev);
return;
@@ -4763,6 +4798,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
 
ufshcd_init_pwr_info(hba);
 
+   /* set the default level for urgent bkops */
+   hba->urgent_bkops_lvl = BKOPS_STATUS_PERF_IMPACT;
+   hba->is_urgent_bkops_lvl_checked = false;
+
/* UniPro link is active now */
ufshcd_set_link_active(hba);
 
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 77e79c0..65f29aa 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -383,6 +383,9 @@ struct ufs_init_prefetch {
  * @clk_list_head: UFS host controller clocks list node head
  * @pwr_info: holds current power mode
  * @max_pwr_info: keeps the device max valid pwm
+ * @urgent_bkops_lvl: keeps track of urgent bkops level for device
+ * @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for
+ *  device is known or not.
  */
 struct ufs_hba {
void __iomem *mmio_base;
@@ -538,6 +541,9 @@ struct ufs_hba {
struct devfreq *devfreq;
struct ufs_clk_scaling clk_scaling;
bool is_sys_suspended;
+
+   enum bkops_status urgent_bkops_lvl;
+   bool is_urgent_bkops_lvl_checked;
 };
 
 /* Returns true if clocks can be gated. Otherwise false */
-- 
1.8.5.2

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


[PATCH v7 11/17] scsi: ufs: handle non spec compliant bkops behaviour by device

2016-03-08 Thread Yaniv Gardi
We are seeing that some devices are raising the urgent bkops exception
events even when BKOPS status doesn't indicate performace impacted or
critical. Handle these device by determining their urgent bkops status
at runtime.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h |  6 ++
 2 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 394054e..d28c629 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3763,7 +3763,7 @@ out:
  */
 static int ufshcd_urgent_bkops(struct ufs_hba *hba)
 {
-   return ufshcd_bkops_ctrl(hba, BKOPS_STATUS_PERF_IMPACT);
+   return ufshcd_bkops_ctrl(hba, hba->urgent_bkops_lvl);
 }
 
 static inline int ufshcd_get_ee_status(struct ufs_hba *hba, u32 *status)
@@ -3772,6 +3772,43 @@ static inline int ufshcd_get_ee_status(struct ufs_hba 
*hba, u32 *status)
QUERY_ATTR_IDN_EE_STATUS, 0, 0, status);
 }
 
+static void ufshcd_bkops_exception_event_handler(struct ufs_hba *hba)
+{
+   int err;
+   u32 curr_status = 0;
+
+   if (hba->is_urgent_bkops_lvl_checked)
+   goto enable_auto_bkops;
+
+   err = ufshcd_get_bkops_status(hba, _status);
+   if (err) {
+   dev_err(hba->dev, "%s: failed to get BKOPS status %d\n",
+   __func__, err);
+   goto out;
+   }
+
+   /*
+* We are seeing that some devices are raising the urgent bkops
+* exception events even when BKOPS status doesn't indicate performace
+* impacted or critical. Handle these device by determining their urgent
+* bkops status at runtime.
+*/
+   if (curr_status < BKOPS_STATUS_PERF_IMPACT) {
+   dev_err(hba->dev, "%s: device raised urgent BKOPS exception for 
bkops status %d\n",
+   __func__, curr_status);
+   /* update the current status as the urgent bkops level */
+   hba->urgent_bkops_lvl = curr_status;
+   hba->is_urgent_bkops_lvl_checked = true;
+   }
+
+enable_auto_bkops:
+   err = ufshcd_enable_auto_bkops(hba);
+out:
+   if (err < 0)
+   dev_err(hba->dev, "%s: failed to handle urgent bkops %d\n",
+   __func__, err);
+}
+
 /**
  * ufshcd_exception_event_handler - handle exceptions raised by device
  * @work: pointer to work data
@@ -3795,12 +3832,10 @@ static void ufshcd_exception_event_handler(struct 
work_struct *work)
}
 
status &= hba->ee_ctrl_mask;
-   if (status & MASK_EE_URGENT_BKOPS) {
-   err = ufshcd_urgent_bkops(hba);
-   if (err < 0)
-   dev_err(hba->dev, "%s: failed to handle urgent bkops 
%d\n",
-   __func__, err);
-   }
+
+   if (status & MASK_EE_URGENT_BKOPS)
+   ufshcd_bkops_exception_event_handler(hba);
+
 out:
pm_runtime_put_sync(hba->dev);
return;
@@ -4763,6 +4798,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
 
ufshcd_init_pwr_info(hba);
 
+   /* set the default level for urgent bkops */
+   hba->urgent_bkops_lvl = BKOPS_STATUS_PERF_IMPACT;
+   hba->is_urgent_bkops_lvl_checked = false;
+
/* UniPro link is active now */
ufshcd_set_link_active(hba);
 
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 77e79c0..65f29aa 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -383,6 +383,9 @@ struct ufs_init_prefetch {
  * @clk_list_head: UFS host controller clocks list node head
  * @pwr_info: holds current power mode
  * @max_pwr_info: keeps the device max valid pwm
+ * @urgent_bkops_lvl: keeps track of urgent bkops level for device
+ * @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for
+ *  device is known or not.
  */
 struct ufs_hba {
void __iomem *mmio_base;
@@ -538,6 +541,9 @@ struct ufs_hba {
struct devfreq *devfreq;
struct ufs_clk_scaling clk_scaling;
bool is_sys_suspended;
+
+   enum bkops_status urgent_bkops_lvl;
+   bool is_urgent_bkops_lvl_checked;
 };
 
 /* Returns true if clocks can be gated. Otherwise false */
-- 
1.8.5.2

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


[PATCH v7 09/17] scsi: ufs: add error recovery after DL NAC error

2016-03-08 Thread Yaniv Gardi
Some vendor's UFS device sends back to back NACs for the DL data frames
causing the host controller to raise the DFES error status. Sometimes
such UFS devices send back to back NAC without waiting for new
retransmitted DL frame from the host and in such cases it might be
possible the Host UniPro goes into bad state without raising the DFES
error interrupt. If this happens then all the pending commands would
timeout only after respective SW command (which is generally too
large).

This change workarounds such device behaviour like this:
- As soon as SW sees the DL NAC error, it would schedule the error
  handler
- Error handler would sleep for 50ms to see if there any fatal errors
  raised by UFS controller.
   - If there are fatal errors then SW does normal error recovery.
   - If there are no fatal errors then SW sends the NOP command to
 device to check if link is alive.
   - If NOP command times out, SW does normal error recovery
   - If NOP command succeed, skip the error handling.

If DL NAC error is seen multiple times with some vendor's UFS devices
then enable this quirk to initiate quick error recovery and also
silence related error logs to reduce spamming of kernel logs.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 93 +++
 drivers/scsi/ufs/ufshci.h |  2 +
 2 files changed, 95 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index dc096f1..e9c7c91 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3795,6 +3795,79 @@ static void ufshcd_complete_requests(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_quirk_dl_nac_errors - This function checks if error handling is
+ * to recover from the DL NAC errors or not.
+ * @hba: per-adapter instance
+ *
+ * Returns true if error handling is required, false otherwise
+ */
+static bool ufshcd_quirk_dl_nac_errors(struct ufs_hba *hba)
+{
+   unsigned long flags;
+   bool err_handling = true;
+
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   /*
+* UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS only workaround the
+* device fatal error and/or DL NAC & REPLAY timeout errors.
+*/
+   if (hba->saved_err & (CONTROLLER_FATAL_ERROR | SYSTEM_BUS_FATAL_ERROR))
+   goto out;
+
+   if ((hba->saved_err & DEVICE_FATAL_ERROR) ||
+   ((hba->saved_err & UIC_ERROR) &&
+(hba->saved_uic_err & UFSHCD_UIC_DL_TCx_REPLAY_ERROR)))
+   goto out;
+
+   if ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)) {
+   int err;
+   /*
+* wait for 50ms to see if we can get any other errors or not.
+*/
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   msleep(50);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   /*
+* now check if we have got any other severe errors other than
+* DL NAC error?
+*/
+   if ((hba->saved_err & INT_FATAL_ERRORS) ||
+   ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)))
+   goto out;
+
+   /*
+* As DL NAC is the only error received so far, send out NOP
+* command to confirm if link is still active or not.
+*   - If we don't get any response then do error recovery.
+*   - If we get response then clear the DL NAC error bit.
+*/
+
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   err = ufshcd_verify_dev_init(hba);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   if (err)
+   goto out;
+
+   /* Link seems to be alive hence ignore the DL NAC errors */
+   if (hba->saved_uic_err == UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)
+   hba->saved_err &= ~UIC_ERROR;
+   /* clear NAC error */
+   hba->saved_uic_err &= ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR;
+   if (!hba->saved_uic_err) {
+   err_handling = false;
+   goto out;
+   }
+   }
+out:
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   return err_handling;
+}
+
+/**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
  */
@@ -3822,6 +3895,17 @@ static void ufshcd_err_handle

[PATCH v7 09/17] scsi: ufs: add error recovery after DL NAC error

2016-03-08 Thread Yaniv Gardi
Some vendor's UFS device sends back to back NACs for the DL data frames
causing the host controller to raise the DFES error status. Sometimes
such UFS devices send back to back NAC without waiting for new
retransmitted DL frame from the host and in such cases it might be
possible the Host UniPro goes into bad state without raising the DFES
error interrupt. If this happens then all the pending commands would
timeout only after respective SW command (which is generally too
large).

This change workarounds such device behaviour like this:
- As soon as SW sees the DL NAC error, it would schedule the error
  handler
- Error handler would sleep for 50ms to see if there any fatal errors
  raised by UFS controller.
   - If there are fatal errors then SW does normal error recovery.
   - If there are no fatal errors then SW sends the NOP command to
 device to check if link is alive.
   - If NOP command times out, SW does normal error recovery
   - If NOP command succeed, skip the error handling.

If DL NAC error is seen multiple times with some vendor's UFS devices
then enable this quirk to initiate quick error recovery and also
silence related error logs to reduce spamming of kernel logs.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 93 +++
 drivers/scsi/ufs/ufshci.h |  2 +
 2 files changed, 95 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index dc096f1..e9c7c91 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3795,6 +3795,79 @@ static void ufshcd_complete_requests(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_quirk_dl_nac_errors - This function checks if error handling is
+ * to recover from the DL NAC errors or not.
+ * @hba: per-adapter instance
+ *
+ * Returns true if error handling is required, false otherwise
+ */
+static bool ufshcd_quirk_dl_nac_errors(struct ufs_hba *hba)
+{
+   unsigned long flags;
+   bool err_handling = true;
+
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   /*
+* UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS only workaround the
+* device fatal error and/or DL NAC & REPLAY timeout errors.
+*/
+   if (hba->saved_err & (CONTROLLER_FATAL_ERROR | SYSTEM_BUS_FATAL_ERROR))
+   goto out;
+
+   if ((hba->saved_err & DEVICE_FATAL_ERROR) ||
+   ((hba->saved_err & UIC_ERROR) &&
+(hba->saved_uic_err & UFSHCD_UIC_DL_TCx_REPLAY_ERROR)))
+   goto out;
+
+   if ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)) {
+   int err;
+   /*
+* wait for 50ms to see if we can get any other errors or not.
+*/
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   msleep(50);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   /*
+* now check if we have got any other severe errors other than
+* DL NAC error?
+*/
+   if ((hba->saved_err & INT_FATAL_ERRORS) ||
+   ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)))
+   goto out;
+
+   /*
+* As DL NAC is the only error received so far, send out NOP
+* command to confirm if link is still active or not.
+*   - If we don't get any response then do error recovery.
+*   - If we get response then clear the DL NAC error bit.
+*/
+
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   err = ufshcd_verify_dev_init(hba);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   if (err)
+   goto out;
+
+   /* Link seems to be alive hence ignore the DL NAC errors */
+   if (hba->saved_uic_err == UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)
+   hba->saved_err &= ~UIC_ERROR;
+   /* clear NAC error */
+   hba->saved_uic_err &= ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR;
+   if (!hba->saved_uic_err) {
+   err_handling = false;
+   goto out;
+   }
+   }
+out:
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   return err_handling;
+}
+
+/**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
  */
@@ -3822,6 +3895,17 @@ static void ufshcd_err_handler(struct work_struct *work)
 
/* Complete requests that have door-bell cleared by h/w

[PATCH v7 02/17] scsi: ufs: avoid spurious UFS host controller interrupts

2016-03-08 Thread Yaniv Gardi
When control reaches to Linux UFS driver during UFS boot mode, UFS host
controller interrupt status/enable registers may have left over
settings.
In order to avoid any spurious interrupts due to these left overs,
it's important to clear these interrupt status/enable registers before
enabling UFS interrupt handling.

Reviewed-by: Hannes Reinecke <h...@suse.de>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 15 +++
 1 file changed, 15 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a8e42df..de7280c 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5837,6 +5837,21 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem 
*mmio_base, unsigned int irq)
init_waitqueue_head(>dev_cmd.tag_wq);
 
ufshcd_init_clk_gating(hba);
+
+   /*
+* In order to avoid any spurious interrupt immediately after
+* registering UFS controller interrupt handler, clear any pending UFS
+* interrupt status and disable all the UFS interrupts.
+*/
+   ufshcd_writel(hba, ufshcd_readl(hba, REG_INTERRUPT_STATUS),
+ REG_INTERRUPT_STATUS);
+   ufshcd_writel(hba, 0, REG_INTERRUPT_ENABLE);
+   /*
+* Make sure that UFS interrupts are disabled and any pending interrupt
+* status is cleared before registering UFS interrupt handler.
+*/
+   mb();
+
/* IRQ registration */
err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
if (err) {
-- 
1.8.5.2

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


[PATCH v7 04/17] scsi: ufs: verify hba controller hce reg value

2016-03-08 Thread Yaniv Gardi
Sometimes due to hw issues it takes some time to the
host controller register to update. In order to verify the register
has updated, a polling is done until its value is set.

In addition the functions ufshcd_hba_stop() and
ufshcd_wait_for_register() was updated with an additional input
parameter, indicating the timeout between reads will
be done by sleeping or spinning the cpu.

Reviewed-by: Hannes Reinecke <h...@suse.com>
Signed-off-by: Raviv Shvili <rshv...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h | 12 +++
 2 files changed, 35 insertions(+), 30 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 3400ceb..80031e6 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -240,11 +240,13 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
  * @val - wait condition
  * @interval_us - polling interval in microsecs
  * @timeout_ms - timeout in millisecs
+ * @can_sleep - perform sleep or just spin
  *
  * 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 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
+   u32 val, unsigned long interval_us,
+   unsigned long timeout_ms, bool can_sleep)
 {
int err = 0;
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
@@ -253,9 +255,10 @@ static int ufshcd_wait_for_register(struct ufs_hba *hba, 
u32 reg, u32 mask,
val = val & mask;
 
while ((ufshcd_readl(hba, reg) & mask) != val) {
-   /* wakeup within 50us of expiry */
-   usleep_range(interval_us, interval_us + 50);
-
+   if (can_sleep)
+   usleep_range(interval_us, interval_us + 50);
+   else
+   udelay(interval_us);
if (time_after(jiffies, timeout)) {
if ((ufshcd_readl(hba, reg) & mask) != val)
err = -ETIMEDOUT;
@@ -1459,7 +1462,7 @@ ufshcd_clear_cmd(struct ufs_hba *hba, int tag)
 */
err = ufshcd_wait_for_register(hba,
REG_UTP_TRANSFER_REQ_DOOR_BELL,
-   mask, ~mask, 1000, 1000);
+   mask, ~mask, 1000, 1000, true);
 
return err;
 }
@@ -2815,6 +2818,23 @@ out:
 }
 
 /**
+ * ufshcd_hba_stop - Send controller to reset state
+ * @hba: per adapter instance
+ * @can_sleep: perform sleep or just spin
+ */
+static inline void ufshcd_hba_stop(struct ufs_hba *hba, bool can_sleep)
+{
+   int err;
+
+   ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
+   err = ufshcd_wait_for_register(hba, REG_CONTROLLER_ENABLE,
+   CONTROLLER_ENABLE, CONTROLLER_DISABLE,
+   10, 1, can_sleep);
+   if (err)
+   dev_err(hba->dev, "%s: Controller disable failed\n", __func__);
+}
+
+/**
  * ufshcd_hba_enable - initialize the controller
  * @hba: per adapter instance
  *
@@ -2834,18 +2854,9 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
 * development and testing of this driver. msleep can be changed to
 * mdelay and retry count can be reduced based on the controller.
 */
-   if (!ufshcd_is_hba_active(hba)) {
-
+   if (!ufshcd_is_hba_active(hba))
/* change controller state to "reset state" */
-   ufshcd_hba_stop(hba);
-
-   /*
-* This delay is based on the testing done with UFS host
-* controller FPGA. The delay can be changed based on the
-* host controller used.
-*/
-   msleep(5);
-   }
+   ufshcd_hba_stop(hba, true);
 
/* UniPro link is disabled at this point */
ufshcd_set_link_off(hba);
@@ -3898,7 +3909,7 @@ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int 
tag)
/* 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,
-   mask, 0, 1000, 1000);
+   mask, 0, 1000, 1000, true);
 out:
return err;
 }
@@ -4180,7 +4191,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba 
*hba)
 
/* Reset the host controller */
spin_lock_irqsave(hba->host->host_lock, flags);
-   ufshcd_hba_stop(hba);
+   ufshcd_hba_stop(hba, false);
spin_unlock_irqrestore(hba->host->host_lock, flags);
 
err = ufshcd_hba_enable(hba);
@@ -5133,7 +5144,7 @@ static int ufshcd_link_state_transition(struct uf

[PATCH v7 02/17] scsi: ufs: avoid spurious UFS host controller interrupts

2016-03-08 Thread Yaniv Gardi
When control reaches to Linux UFS driver during UFS boot mode, UFS host
controller interrupt status/enable registers may have left over
settings.
In order to avoid any spurious interrupts due to these left overs,
it's important to clear these interrupt status/enable registers before
enabling UFS interrupt handling.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 15 +++
 1 file changed, 15 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a8e42df..de7280c 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5837,6 +5837,21 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem 
*mmio_base, unsigned int irq)
init_waitqueue_head(>dev_cmd.tag_wq);
 
ufshcd_init_clk_gating(hba);
+
+   /*
+* In order to avoid any spurious interrupt immediately after
+* registering UFS controller interrupt handler, clear any pending UFS
+* interrupt status and disable all the UFS interrupts.
+*/
+   ufshcd_writel(hba, ufshcd_readl(hba, REG_INTERRUPT_STATUS),
+ REG_INTERRUPT_STATUS);
+   ufshcd_writel(hba, 0, REG_INTERRUPT_ENABLE);
+   /*
+* Make sure that UFS interrupts are disabled and any pending interrupt
+* status is cleared before registering UFS interrupt handler.
+*/
+   mb();
+
/* IRQ registration */
err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
if (err) {
-- 
1.8.5.2

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


[PATCH v7 04/17] scsi: ufs: verify hba controller hce reg value

2016-03-08 Thread Yaniv Gardi
Sometimes due to hw issues it takes some time to the
host controller register to update. In order to verify the register
has updated, a polling is done until its value is set.

In addition the functions ufshcd_hba_stop() and
ufshcd_wait_for_register() was updated with an additional input
parameter, indicating the timeout between reads will
be done by sleeping or spinning the cpu.

Reviewed-by: Hannes Reinecke 
Signed-off-by: Raviv Shvili 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h | 12 +++
 2 files changed, 35 insertions(+), 30 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 3400ceb..80031e6 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -240,11 +240,13 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
  * @val - wait condition
  * @interval_us - polling interval in microsecs
  * @timeout_ms - timeout in millisecs
+ * @can_sleep - perform sleep or just spin
  *
  * 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 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
+   u32 val, unsigned long interval_us,
+   unsigned long timeout_ms, bool can_sleep)
 {
int err = 0;
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
@@ -253,9 +255,10 @@ static int ufshcd_wait_for_register(struct ufs_hba *hba, 
u32 reg, u32 mask,
val = val & mask;
 
while ((ufshcd_readl(hba, reg) & mask) != val) {
-   /* wakeup within 50us of expiry */
-   usleep_range(interval_us, interval_us + 50);
-
+   if (can_sleep)
+   usleep_range(interval_us, interval_us + 50);
+   else
+   udelay(interval_us);
if (time_after(jiffies, timeout)) {
if ((ufshcd_readl(hba, reg) & mask) != val)
err = -ETIMEDOUT;
@@ -1459,7 +1462,7 @@ ufshcd_clear_cmd(struct ufs_hba *hba, int tag)
 */
err = ufshcd_wait_for_register(hba,
REG_UTP_TRANSFER_REQ_DOOR_BELL,
-   mask, ~mask, 1000, 1000);
+   mask, ~mask, 1000, 1000, true);
 
return err;
 }
@@ -2815,6 +2818,23 @@ out:
 }
 
 /**
+ * ufshcd_hba_stop - Send controller to reset state
+ * @hba: per adapter instance
+ * @can_sleep: perform sleep or just spin
+ */
+static inline void ufshcd_hba_stop(struct ufs_hba *hba, bool can_sleep)
+{
+   int err;
+
+   ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
+   err = ufshcd_wait_for_register(hba, REG_CONTROLLER_ENABLE,
+   CONTROLLER_ENABLE, CONTROLLER_DISABLE,
+   10, 1, can_sleep);
+   if (err)
+   dev_err(hba->dev, "%s: Controller disable failed\n", __func__);
+}
+
+/**
  * ufshcd_hba_enable - initialize the controller
  * @hba: per adapter instance
  *
@@ -2834,18 +2854,9 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
 * development and testing of this driver. msleep can be changed to
 * mdelay and retry count can be reduced based on the controller.
 */
-   if (!ufshcd_is_hba_active(hba)) {
-
+   if (!ufshcd_is_hba_active(hba))
/* change controller state to "reset state" */
-   ufshcd_hba_stop(hba);
-
-   /*
-* This delay is based on the testing done with UFS host
-* controller FPGA. The delay can be changed based on the
-* host controller used.
-*/
-   msleep(5);
-   }
+   ufshcd_hba_stop(hba, true);
 
/* UniPro link is disabled at this point */
ufshcd_set_link_off(hba);
@@ -3898,7 +3909,7 @@ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int 
tag)
/* 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,
-   mask, 0, 1000, 1000);
+   mask, 0, 1000, 1000, true);
 out:
return err;
 }
@@ -4180,7 +4191,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba 
*hba)
 
/* Reset the host controller */
spin_lock_irqsave(hba->host->host_lock, flags);
-   ufshcd_hba_stop(hba);
+   ufshcd_hba_stop(hba, false);
spin_unlock_irqrestore(hba->host->host_lock, flags);
 
err = ufshcd_hba_enable(hba);
@@ -5133,7 +5144,7 @@ static int ufshcd_link_state_transition(struct ufs_hba 
*hba,
 * Change controller state to "reset state"

[PATCH v7 00/17] add fixes, device quirks, error recovery,

2016-03-08 Thread Yaniv Gardi
V7:
updated patches with "Reviewed-by" and "Acked-by" tags

V6:
as per comment, patch 15/15 in V5 was divided into 3 patches in V6

V5:
as per comment, patch 05/14 in V4, was divided into 2 patches in V5

V4:
fixed DOS EOL symbols in 2 new files

V3:
Due to comments in V2
Removed change 02/17 of V2 
Removed change 03/17 of V2
Removed change 17/17 of V2

V2:
This series should be pushed on top of 15 patches series:
"Big fixes, retries, handle a race condition"
fixed and few comments.

V1:
This series should be pushed on top of 15 patches series:
"Big fixes, retries, handle a race condition"


Yaniv Gardi (15):
  scsi: ufs-qcom: add number of lanes per direction
  scsi: ufs: avoid spurious UFS host controller interrupts
  scsi: ufs: implement scsi host timeout handler
  scsi: ufs: verify hba controller hce reg value
  scsi: ufs: add support to read device and string descriptors
  scsi: ufs: separate device and host quirks
  scsi: ufs: disable vccq if it's not needed by UFS device
  scsi: ufs: make error handling bit faster
  scsi: ufs: add error recovery after DL NAC error
  scsi: ufs: add retry for query descriptors
  scsi: ufs: handle non spec compliant bkops behaviour by device
  scsi: ufs: tune UniPro parameters to optimize hibern8 exit time
  scsi: ufs: fix leakage during link off state
  scsi: ufs: add device quirk delay before putting UFS rails in LPM
  scsi: ufs-qcom: set PA_Local_TX_LCC_Enable before link startup
  scsi: ufs-qcom: enable/disable the device ref clock
  scsi: ufs-qcom: add printouts of testbus debug registers

 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |   3 +
 drivers/scsi/ufs/Makefile  |   2 +-
 drivers/scsi/ufs/ufs-qcom.c| 155 -
 drivers/scsi/ufs/ufs-qcom.h|   9 +
 drivers/scsi/ufs/ufs.h |  33 +
 drivers/scsi/ufs/ufs_quirks.c  | 104 +++
 drivers/scsi/ufs/ufs_quirks.h  | 133 
 drivers/scsi/ufs/ufshcd-pltfrm.c   |  19 +
 drivers/scsi/ufs/ufshcd.c  | 749 ++---
 drivers/scsi/ufs/ufshcd.h  |  40 +-
 drivers/scsi/ufs/ufshci.h  |   4 +
 drivers/scsi/ufs/unipro.h  |  22 +
 12 files changed, 1145 insertions(+), 128 deletions(-)
 create mode 100644 drivers/scsi/ufs/ufs_quirks.c
 create mode 100644 drivers/scsi/ufs/ufs_quirks.h

-- 
1.8.5.2

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


[PATCH v7 00/17] add fixes, device quirks, error recovery,

2016-03-08 Thread Yaniv Gardi
V7:
updated patches with "Reviewed-by" and "Acked-by" tags

V6:
as per comment, patch 15/15 in V5 was divided into 3 patches in V6

V5:
as per comment, patch 05/14 in V4, was divided into 2 patches in V5

V4:
fixed DOS EOL symbols in 2 new files

V3:
Due to comments in V2
Removed change 02/17 of V2 
Removed change 03/17 of V2
Removed change 17/17 of V2

V2:
This series should be pushed on top of 15 patches series:
"Big fixes, retries, handle a race condition"
fixed and few comments.

V1:
This series should be pushed on top of 15 patches series:
"Big fixes, retries, handle a race condition"


Yaniv Gardi (15):
  scsi: ufs-qcom: add number of lanes per direction
  scsi: ufs: avoid spurious UFS host controller interrupts
  scsi: ufs: implement scsi host timeout handler
  scsi: ufs: verify hba controller hce reg value
  scsi: ufs: add support to read device and string descriptors
  scsi: ufs: separate device and host quirks
  scsi: ufs: disable vccq if it's not needed by UFS device
  scsi: ufs: make error handling bit faster
  scsi: ufs: add error recovery after DL NAC error
  scsi: ufs: add retry for query descriptors
  scsi: ufs: handle non spec compliant bkops behaviour by device
  scsi: ufs: tune UniPro parameters to optimize hibern8 exit time
  scsi: ufs: fix leakage during link off state
  scsi: ufs: add device quirk delay before putting UFS rails in LPM
  scsi: ufs-qcom: set PA_Local_TX_LCC_Enable before link startup
  scsi: ufs-qcom: enable/disable the device ref clock
  scsi: ufs-qcom: add printouts of testbus debug registers

 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt  |   3 +
 drivers/scsi/ufs/Makefile  |   2 +-
 drivers/scsi/ufs/ufs-qcom.c| 155 -
 drivers/scsi/ufs/ufs-qcom.h|   9 +
 drivers/scsi/ufs/ufs.h |  33 +
 drivers/scsi/ufs/ufs_quirks.c  | 104 +++
 drivers/scsi/ufs/ufs_quirks.h  | 133 
 drivers/scsi/ufs/ufshcd-pltfrm.c   |  19 +
 drivers/scsi/ufs/ufshcd.c  | 749 ++---
 drivers/scsi/ufs/ufshcd.h  |  40 +-
 drivers/scsi/ufs/ufshci.h  |   4 +
 drivers/scsi/ufs/unipro.h  |  22 +
 12 files changed, 1145 insertions(+), 128 deletions(-)
 create mode 100644 drivers/scsi/ufs/ufs_quirks.c
 create mode 100644 drivers/scsi/ufs/ufs_quirks.h

-- 
1.8.5.2

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


[PATCH v6 04/17] scsi: ufs: verify hba controller hce reg value

2016-03-06 Thread Yaniv Gardi
Sometimes due to hw issues it takes some time to the
host controller register to update. In order to verify the register
has updated, a polling is done until its value is set.

In addition the functions ufshcd_hba_stop() and
ufshcd_wait_for_register() was updated with an additional input
parameter, indicating the timeout between reads will
be done by sleeping or spinning the cpu.

Signed-off-by: Raviv Shvili <rshv...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h | 12 +++
 2 files changed, 35 insertions(+), 30 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 3400ceb..80031e6 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -240,11 +240,13 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
  * @val - wait condition
  * @interval_us - polling interval in microsecs
  * @timeout_ms - timeout in millisecs
+ * @can_sleep - perform sleep or just spin
  *
  * 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 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
+   u32 val, unsigned long interval_us,
+   unsigned long timeout_ms, bool can_sleep)
 {
int err = 0;
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
@@ -253,9 +255,10 @@ static int ufshcd_wait_for_register(struct ufs_hba *hba, 
u32 reg, u32 mask,
val = val & mask;
 
while ((ufshcd_readl(hba, reg) & mask) != val) {
-   /* wakeup within 50us of expiry */
-   usleep_range(interval_us, interval_us + 50);
-
+   if (can_sleep)
+   usleep_range(interval_us, interval_us + 50);
+   else
+   udelay(interval_us);
if (time_after(jiffies, timeout)) {
if ((ufshcd_readl(hba, reg) & mask) != val)
err = -ETIMEDOUT;
@@ -1459,7 +1462,7 @@ ufshcd_clear_cmd(struct ufs_hba *hba, int tag)
 */
err = ufshcd_wait_for_register(hba,
REG_UTP_TRANSFER_REQ_DOOR_BELL,
-   mask, ~mask, 1000, 1000);
+   mask, ~mask, 1000, 1000, true);
 
return err;
 }
@@ -2815,6 +2818,23 @@ out:
 }
 
 /**
+ * ufshcd_hba_stop - Send controller to reset state
+ * @hba: per adapter instance
+ * @can_sleep: perform sleep or just spin
+ */
+static inline void ufshcd_hba_stop(struct ufs_hba *hba, bool can_sleep)
+{
+   int err;
+
+   ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
+   err = ufshcd_wait_for_register(hba, REG_CONTROLLER_ENABLE,
+   CONTROLLER_ENABLE, CONTROLLER_DISABLE,
+   10, 1, can_sleep);
+   if (err)
+   dev_err(hba->dev, "%s: Controller disable failed\n", __func__);
+}
+
+/**
  * ufshcd_hba_enable - initialize the controller
  * @hba: per adapter instance
  *
@@ -2834,18 +2854,9 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
 * development and testing of this driver. msleep can be changed to
 * mdelay and retry count can be reduced based on the controller.
 */
-   if (!ufshcd_is_hba_active(hba)) {
-
+   if (!ufshcd_is_hba_active(hba))
/* change controller state to "reset state" */
-   ufshcd_hba_stop(hba);
-
-   /*
-* This delay is based on the testing done with UFS host
-* controller FPGA. The delay can be changed based on the
-* host controller used.
-*/
-   msleep(5);
-   }
+   ufshcd_hba_stop(hba, true);
 
/* UniPro link is disabled at this point */
ufshcd_set_link_off(hba);
@@ -3898,7 +3909,7 @@ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int 
tag)
/* 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,
-   mask, 0, 1000, 1000);
+   mask, 0, 1000, 1000, true);
 out:
return err;
 }
@@ -4180,7 +4191,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba 
*hba)
 
/* Reset the host controller */
spin_lock_irqsave(hba->host->host_lock, flags);
-   ufshcd_hba_stop(hba);
+   ufshcd_hba_stop(hba, false);
spin_unlock_irqrestore(hba->host->host_lock, flags);
 
err = ufshcd_hba_enable(hba);
@@ -5133,7 +5144,7 @@ static int ufshcd_link_state_transition(struct ufs_hba 
*hba,
 * Change controller 

[PATCH v6 04/17] scsi: ufs: verify hba controller hce reg value

2016-03-06 Thread Yaniv Gardi
Sometimes due to hw issues it takes some time to the
host controller register to update. In order to verify the register
has updated, a polling is done until its value is set.

In addition the functions ufshcd_hba_stop() and
ufshcd_wait_for_register() was updated with an additional input
parameter, indicating the timeout between reads will
be done by sleeping or spinning the cpu.

Signed-off-by: Raviv Shvili 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h | 12 +++
 2 files changed, 35 insertions(+), 30 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 3400ceb..80031e6 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -240,11 +240,13 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
  * @val - wait condition
  * @interval_us - polling interval in microsecs
  * @timeout_ms - timeout in millisecs
+ * @can_sleep - perform sleep or just spin
  *
  * 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 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
+   u32 val, unsigned long interval_us,
+   unsigned long timeout_ms, bool can_sleep)
 {
int err = 0;
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
@@ -253,9 +255,10 @@ static int ufshcd_wait_for_register(struct ufs_hba *hba, 
u32 reg, u32 mask,
val = val & mask;
 
while ((ufshcd_readl(hba, reg) & mask) != val) {
-   /* wakeup within 50us of expiry */
-   usleep_range(interval_us, interval_us + 50);
-
+   if (can_sleep)
+   usleep_range(interval_us, interval_us + 50);
+   else
+   udelay(interval_us);
if (time_after(jiffies, timeout)) {
if ((ufshcd_readl(hba, reg) & mask) != val)
err = -ETIMEDOUT;
@@ -1459,7 +1462,7 @@ ufshcd_clear_cmd(struct ufs_hba *hba, int tag)
 */
err = ufshcd_wait_for_register(hba,
REG_UTP_TRANSFER_REQ_DOOR_BELL,
-   mask, ~mask, 1000, 1000);
+   mask, ~mask, 1000, 1000, true);
 
return err;
 }
@@ -2815,6 +2818,23 @@ out:
 }
 
 /**
+ * ufshcd_hba_stop - Send controller to reset state
+ * @hba: per adapter instance
+ * @can_sleep: perform sleep or just spin
+ */
+static inline void ufshcd_hba_stop(struct ufs_hba *hba, bool can_sleep)
+{
+   int err;
+
+   ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
+   err = ufshcd_wait_for_register(hba, REG_CONTROLLER_ENABLE,
+   CONTROLLER_ENABLE, CONTROLLER_DISABLE,
+   10, 1, can_sleep);
+   if (err)
+   dev_err(hba->dev, "%s: Controller disable failed\n", __func__);
+}
+
+/**
  * ufshcd_hba_enable - initialize the controller
  * @hba: per adapter instance
  *
@@ -2834,18 +2854,9 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
 * development and testing of this driver. msleep can be changed to
 * mdelay and retry count can be reduced based on the controller.
 */
-   if (!ufshcd_is_hba_active(hba)) {
-
+   if (!ufshcd_is_hba_active(hba))
/* change controller state to "reset state" */
-   ufshcd_hba_stop(hba);
-
-   /*
-* This delay is based on the testing done with UFS host
-* controller FPGA. The delay can be changed based on the
-* host controller used.
-*/
-   msleep(5);
-   }
+   ufshcd_hba_stop(hba, true);
 
/* UniPro link is disabled at this point */
ufshcd_set_link_off(hba);
@@ -3898,7 +3909,7 @@ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int 
tag)
/* 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,
-   mask, 0, 1000, 1000);
+   mask, 0, 1000, 1000, true);
 out:
return err;
 }
@@ -4180,7 +4191,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba 
*hba)
 
/* Reset the host controller */
spin_lock_irqsave(hba->host->host_lock, flags);
-   ufshcd_hba_stop(hba);
+   ufshcd_hba_stop(hba, false);
spin_unlock_irqrestore(hba->host->host_lock, flags);
 
err = ufshcd_hba_enable(hba);
@@ -5133,7 +5144,7 @@ static int ufshcd_link_state_transition(struct ufs_hba 
*hba,
 * Change controller state to "reset state" which
 

[PATCH v6 14/17] scsi: ufs: add device quirk delay before putting UFS rails in LPM

2016-03-06 Thread Yaniv Gardi
We put the UFS device in sleep state & UFS link in hibern8 state during
runtime suspaned. After this we put all the UFS rails in low power
modes immediately but it seems some devices may still draw more than
sleep current from UFS rails (especially from VCCQ rail) atleast for
500us.
To avoid this situation, this change adds 2ms delay before putting
these UFS rails in LPM mode.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs_quirks.c |  4 
 drivers/scsi/ufs/ufs_quirks.h | 11 ++-
 drivers/scsi/ufs/ufshcd.c | 10 ++
 3 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c
index 476ed01..af68007 100644
--- a/drivers/scsi/ufs/ufs_quirks.c
+++ b/drivers/scsi/ufs/ufs_quirks.c
@@ -16,11 +16,15 @@
 
 static struct ufs_dev_fix ufs_fixups[] = {
/* UFS cards deviations table */
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_NO_FASTAUTO),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
UFS_DEVICE_QUIRK_PA_TACTIVATE),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h
index 01ab166..ffed0da 100644
--- a/drivers/scsi/ufs/ufs_quirks.h
+++ b/drivers/scsi/ufs/ufs_quirks.h
@@ -18,7 +18,7 @@
 /* return true if s1 is a prefix of s2 */
 #define STR_PRFX_EQUAL(s1, s2) !strncmp(s1, s2, strlen(s1))
 
-#define UFS_ANY_VENDOR -1
+#define UFS_ANY_VENDOR 0x
 #define UFS_ANY_MODEL  "ANY_MODEL"
 
 #define MAX_MODEL_LEN 16
@@ -119,6 +119,15 @@ struct ufs_dev_fix {
  */
 #define UFS_DEVICE_NO_FASTAUTO (1 << 5)
 
+/*
+ * It seems some UFS devices may keep drawing more than sleep current
+ * (atleast for 500us) from UFS rails (especially from VCCQ rail).
+ * To avoid this situation, add 2ms delay before putting these UFS
+ * rails in LPM mode.
+ */
+#define UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM  (1 << 6)
+
+
 struct ufs_hba;
 void ufs_advertise_fixup_device(struct ufs_hba *hba);
 #endif /* UFS_QUIRKS_H_ */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index f108a0b..da882cf 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5647,6 +5647,16 @@ out:
 static void ufshcd_vreg_set_lpm(struct ufs_hba *hba)
 {
/*
+* It seems some UFS devices may keep drawing more than sleep current
+* (atleast for 500us) from UFS rails (especially from VCCQ rail).
+* To avoid this situation, add 2ms delay before putting these UFS
+* rails in LPM mode.
+*/
+   if (!ufshcd_is_link_active(hba) &&
+   hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM)
+   usleep_range(2000, 2100);
+
+   /*
 * If UFS device is either in UFS_Sleep turn off VCC rail to save some
 * power.
 *
-- 
1.8.5.2

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


[PATCH v6 08/17] scsi: ufs: make error handling bit faster

2016-03-06 Thread Yaniv Gardi
UFS driver's error handler forcefully tries to clear all the pending
requests. For each pending request in the queue, it waits 1 sec for it
to get cleared. If we have multiple requests in the queue then it's
possible that we might end up waiting for those many seconds before
resetting the host. But note that resetting host would any way clear
all the pending requests from the hardware. Hence this change skips
the forceful clear of the pending requests if we are anyway going to
reset the host (for fatal errors).

Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 155 +-
 1 file changed, 112 insertions(+), 43 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 987cf27..dc096f1 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -133,9 +133,11 @@ enum {
 /* 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 */
+   UFSHCD_UIC_DL_NAC_RECEIVED_ERROR = (1 << 1), /* Data link layer error */
+   UFSHCD_UIC_DL_TCx_REPLAY_ERROR = (1 << 2), /* Data link layer error */
+   UFSHCD_UIC_NL_ERROR = (1 << 3), /* Network layer error */
+   UFSHCD_UIC_TL_ERROR = (1 << 4), /* Transport Layer error */
+   UFSHCD_UIC_DME_ERROR = (1 << 5), /* DME error */
 };
 
 /* Interrupt configuration options */
@@ -3465,31 +3467,18 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, 
u32 intr_status)
 }
 
 /**
- * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * __ufshcd_transfer_req_compl - handle SCSI and query command completion
  * @hba: per adapter instance
+ * @completed_reqs: requests to complete
  */
-static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
+   unsigned long completed_reqs)
 {
struct ufshcd_lrb *lrbp;
struct scsi_cmnd *cmd;
-   unsigned long completed_reqs;
-   u32 tr_doorbell;
int result;
int index;
 
-   /* Resetting interrupt aggregation counters first and reading the
-* DOOR_BELL afterward allows us to handle all the completed requests.
-* In order to prevent other interrupts starvation the DB is read once
-* after reset. The down side of this solution is the possibility of
-* false interrupt if device completes another request after resetting
-* aggregation and before reading the DB.
-*/
-   if (ufshcd_is_intr_aggr_allowed(hba))
-   ufshcd_reset_intr_aggr(hba);
-
-   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
-   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
-
for_each_set_bit(index, _reqs, hba->nutrs) {
lrbp = >lrb[index];
cmd = lrbp->cmd;
@@ -3519,6 +3508,31 @@ static void ufshcd_transfer_req_compl(struct ufs_hba 
*hba)
 }
 
 /**
+ * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+{
+   unsigned long completed_reqs;
+   u32 tr_doorbell;
+
+   /* Resetting interrupt aggregation counters first and reading the
+* DOOR_BELL afterward allows us to handle all the completed requests.
+* In order to prevent other interrupts starvation the DB is read once
+* after reset. The down side of this solution is the possibility of
+* false interrupt if device completes another request after resetting
+* aggregation and before reading the DB.
+*/
+   if (ufshcd_is_intr_aggr_allowed(hba))
+   ufshcd_reset_intr_aggr(hba);
+
+   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
+
+   __ufshcd_transfer_req_compl(hba, completed_reqs);
+}
+
+/**
  * ufshcd_disable_ee - disable exception event
  * @hba: per-adapter instance
  * @mask: exception event to disable
@@ -3773,6 +3787,13 @@ out:
return;
 }
 
+/* Complete requests that have door-bell cleared */
+static void ufshcd_complete_requests(struct ufs_hba *hba)
+{
+   ufshcd_transfer_req_compl(hba);
+   ufshcd_tmc_handler(hba);
+}
+
 /**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
@@ -3785,6 +3806,7 @@ static void ufshcd_err_handler(struct work_struct *work)
u32 err_tm = 0;
int err = 0;
int tag;
+   bool needs_reset = false;
 
hba = c

[PATCH v6 14/17] scsi: ufs: add device quirk delay before putting UFS rails in LPM

2016-03-06 Thread Yaniv Gardi
We put the UFS device in sleep state & UFS link in hibern8 state during
runtime suspaned. After this we put all the UFS rails in low power
modes immediately but it seems some devices may still draw more than
sleep current from UFS rails (especially from VCCQ rail) atleast for
500us.
To avoid this situation, this change adds 2ms delay before putting
these UFS rails in LPM mode.

Reviewed-by: Gilad Broner 
Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs_quirks.c |  4 
 drivers/scsi/ufs/ufs_quirks.h | 11 ++-
 drivers/scsi/ufs/ufshcd.c | 10 ++
 3 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c
index 476ed01..af68007 100644
--- a/drivers/scsi/ufs/ufs_quirks.c
+++ b/drivers/scsi/ufs/ufs_quirks.c
@@ -16,11 +16,15 @@
 
 static struct ufs_dev_fix ufs_fixups[] = {
/* UFS cards deviations table */
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_NO_FASTAUTO),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
UFS_DEVICE_QUIRK_PA_TACTIVATE),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h
index 01ab166..ffed0da 100644
--- a/drivers/scsi/ufs/ufs_quirks.h
+++ b/drivers/scsi/ufs/ufs_quirks.h
@@ -18,7 +18,7 @@
 /* return true if s1 is a prefix of s2 */
 #define STR_PRFX_EQUAL(s1, s2) !strncmp(s1, s2, strlen(s1))
 
-#define UFS_ANY_VENDOR -1
+#define UFS_ANY_VENDOR 0x
 #define UFS_ANY_MODEL  "ANY_MODEL"
 
 #define MAX_MODEL_LEN 16
@@ -119,6 +119,15 @@ struct ufs_dev_fix {
  */
 #define UFS_DEVICE_NO_FASTAUTO (1 << 5)
 
+/*
+ * It seems some UFS devices may keep drawing more than sleep current
+ * (atleast for 500us) from UFS rails (especially from VCCQ rail).
+ * To avoid this situation, add 2ms delay before putting these UFS
+ * rails in LPM mode.
+ */
+#define UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM  (1 << 6)
+
+
 struct ufs_hba;
 void ufs_advertise_fixup_device(struct ufs_hba *hba);
 #endif /* UFS_QUIRKS_H_ */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index f108a0b..da882cf 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5647,6 +5647,16 @@ out:
 static void ufshcd_vreg_set_lpm(struct ufs_hba *hba)
 {
/*
+* It seems some UFS devices may keep drawing more than sleep current
+* (atleast for 500us) from UFS rails (especially from VCCQ rail).
+* To avoid this situation, add 2ms delay before putting these UFS
+* rails in LPM mode.
+*/
+   if (!ufshcd_is_link_active(hba) &&
+   hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM)
+   usleep_range(2000, 2100);
+
+   /*
 * If UFS device is either in UFS_Sleep turn off VCC rail to save some
 * power.
 *
-- 
1.8.5.2

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


[PATCH v6 08/17] scsi: ufs: make error handling bit faster

2016-03-06 Thread Yaniv Gardi
UFS driver's error handler forcefully tries to clear all the pending
requests. For each pending request in the queue, it waits 1 sec for it
to get cleared. If we have multiple requests in the queue then it's
possible that we might end up waiting for those many seconds before
resetting the host. But note that resetting host would any way clear
all the pending requests from the hardware. Hence this change skips
the forceful clear of the pending requests if we are anyway going to
reset the host (for fatal errors).

Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 155 +-
 1 file changed, 112 insertions(+), 43 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 987cf27..dc096f1 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -133,9 +133,11 @@ enum {
 /* 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 */
+   UFSHCD_UIC_DL_NAC_RECEIVED_ERROR = (1 << 1), /* Data link layer error */
+   UFSHCD_UIC_DL_TCx_REPLAY_ERROR = (1 << 2), /* Data link layer error */
+   UFSHCD_UIC_NL_ERROR = (1 << 3), /* Network layer error */
+   UFSHCD_UIC_TL_ERROR = (1 << 4), /* Transport Layer error */
+   UFSHCD_UIC_DME_ERROR = (1 << 5), /* DME error */
 };
 
 /* Interrupt configuration options */
@@ -3465,31 +3467,18 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, 
u32 intr_status)
 }
 
 /**
- * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * __ufshcd_transfer_req_compl - handle SCSI and query command completion
  * @hba: per adapter instance
+ * @completed_reqs: requests to complete
  */
-static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
+   unsigned long completed_reqs)
 {
struct ufshcd_lrb *lrbp;
struct scsi_cmnd *cmd;
-   unsigned long completed_reqs;
-   u32 tr_doorbell;
int result;
int index;
 
-   /* Resetting interrupt aggregation counters first and reading the
-* DOOR_BELL afterward allows us to handle all the completed requests.
-* In order to prevent other interrupts starvation the DB is read once
-* after reset. The down side of this solution is the possibility of
-* false interrupt if device completes another request after resetting
-* aggregation and before reading the DB.
-*/
-   if (ufshcd_is_intr_aggr_allowed(hba))
-   ufshcd_reset_intr_aggr(hba);
-
-   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
-   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
-
for_each_set_bit(index, _reqs, hba->nutrs) {
lrbp = >lrb[index];
cmd = lrbp->cmd;
@@ -3519,6 +3508,31 @@ static void ufshcd_transfer_req_compl(struct ufs_hba 
*hba)
 }
 
 /**
+ * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+{
+   unsigned long completed_reqs;
+   u32 tr_doorbell;
+
+   /* Resetting interrupt aggregation counters first and reading the
+* DOOR_BELL afterward allows us to handle all the completed requests.
+* In order to prevent other interrupts starvation the DB is read once
+* after reset. The down side of this solution is the possibility of
+* false interrupt if device completes another request after resetting
+* aggregation and before reading the DB.
+*/
+   if (ufshcd_is_intr_aggr_allowed(hba))
+   ufshcd_reset_intr_aggr(hba);
+
+   tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+   completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
+
+   __ufshcd_transfer_req_compl(hba, completed_reqs);
+}
+
+/**
  * ufshcd_disable_ee - disable exception event
  * @hba: per-adapter instance
  * @mask: exception event to disable
@@ -3773,6 +3787,13 @@ out:
return;
 }
 
+/* Complete requests that have door-bell cleared */
+static void ufshcd_complete_requests(struct ufs_hba *hba)
+{
+   ufshcd_transfer_req_compl(hba);
+   ufshcd_tmc_handler(hba);
+}
+
 /**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
@@ -3785,6 +3806,7 @@ static void ufshcd_err_handler(struct work_struct *work)
u32 err_tm = 0;
int err = 0;
int tag;
+   bool needs_reset = false;
 
hba = container_of(work, struct ufs_hba, eh_work);
 
@@ -3792,40 +3814,75 @@ static 

[PATCH v6 15/17] scsi: ufs-qcom: set PA_Local_TX_LCC_Enable before link startup

2016-03-06 Thread Yaniv Gardi
Some UFS devices (and may be host) have issues if LCC is
enabled. So we are setting PA_Local_TX_LCC_Enable to 0
before link startup which will make sure that both host
and device TX LCC are disabled once link startup is
completed.

Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs-qcom.c | 27 ---
 drivers/scsi/ufs/unipro.h   |  1 +
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index ed57729..966bacf 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -16,8 +16,8 @@
 #include 
 #include 
 #include 
-
 #include 
+
 #include "ufshcd.h"
 #include "ufshcd-pltfrm.h"
 #include "unipro.h"
@@ -106,9 +106,11 @@ static void ufs_qcom_disable_lane_clks(struct 
ufs_qcom_host *host)
if (!host->is_lane_clks_enabled)
return;
 
-   clk_disable_unprepare(host->tx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->tx_l1_sync_clk);
clk_disable_unprepare(host->tx_l0_sync_clk);
-   clk_disable_unprepare(host->rx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->rx_l1_sync_clk);
clk_disable_unprepare(host->rx_l0_sync_clk);
 
host->is_lane_clks_enabled = false;
@@ -272,9 +274,8 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B);
 
if (ret) {
-   dev_err(hba->dev,
-   "%s: ufs_qcom_phy_calibrate_phy()failed, ret = %d\n",
-   __func__, ret);
+   dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret 
= %d\n",
+   __func__, ret);
goto out;
}
 
@@ -524,6 +525,18 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba 
*hba,
err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba,
  150);
 
+   /*
+* Some UFS devices (and may be host) have issues if LCC is
+* enabled. So we are setting PA_Local_TX_LCC_Enable to 0
+* before link startup which will make sure that both host
+* and device TX LCC are disabled once link startup is
+* completed.
+*/
+   if (ufshcd_get_local_unipro_ver(hba) != UFS_UNIPRO_VER_1_41)
+   err = ufshcd_dme_set(hba,
+   UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE),
+   0);
+
break;
case POST_CHANGE:
ufs_qcom_link_startup_post_change(hba);
@@ -1542,7 +1555,7 @@ static int ufs_qcom_probe(struct platform_device *pdev)
  * ufs_qcom_remove - set driver_data of the device to NULL
  * @pdev: pointer to platform device handle
  *
- * Always return 0
+ * Always returns 0
  */
 static int ufs_qcom_remove(struct platform_device *pdev)
 {
diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h
index 8082020..e2854e4 100644
--- a/drivers/scsi/ufs/unipro.h
+++ b/drivers/scsi/ufs/unipro.h
@@ -75,6 +75,7 @@
 #define PA_MAXRXSPEEDFAST  0x1541
 #define PA_MAXRXSPEEDSLOW  0x1542
 #define PA_TXLINKSTARTUPHS 0x1544
+#define PA_LOCAL_TX_LCC_ENABLE 0x155E
 #define PA_TXSPEEDFAST 0x1565
 #define PA_TXSPEEDSLOW 0x1566
 #define PA_REMOTEVERINFO   0x15A0
-- 
1.8.5.2

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


[PATCH v6 10/17] scsi: ufs: add retry for query descriptors

2016-03-06 Thread Yaniv Gardi
Query commands have 100ms timeout and it may timeout if they are
issued in parallel to ongoing read/write SCSI commands, this change
adds the retry (max: 10) in case command timeouts.

Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 55 +++
 1 file changed, 37 insertions(+), 18 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index e9c7c91..394054e 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1875,21 +1875,7 @@ static int ufshcd_query_attr_retry(struct ufs_hba *hba,
return ret;
 }
 
-/**
- * ufshcd_query_descriptor - API function for sending descriptor requests
- * hba: per-adapter instance
- * opcode: attribute opcode
- * idn: attribute idn to access
- * index: index field
- * selector: selector field
- * desc_buf: the buffer that contains the descriptor
- * buf_len: length parameter passed to the device
- *
- * Returns 0 for success, non-zero in case of failure.
- * The buf_len parameter will contain, on return, the length parameter
- * received on the response.
- */
-static int ufshcd_query_descriptor(struct ufs_hba *hba,
+static int __ufshcd_query_descriptor(struct ufs_hba *hba,
enum query_opcode opcode, enum desc_idn idn, u8 index,
u8 selector, u8 *desc_buf, int *buf_len)
 {
@@ -1954,6 +1940,39 @@ out:
 }
 
 /**
+ * ufshcd_query_descriptor_retry - API function for sending descriptor
+ * requests
+ * hba: per-adapter instance
+ * opcode: attribute opcode
+ * idn: attribute idn to access
+ * index: index field
+ * selector: selector field
+ * desc_buf: the buffer that contains the descriptor
+ * buf_len: length parameter passed to the device
+ *
+ * Returns 0 for success, non-zero in case of failure.
+ * The buf_len parameter will contain, on return, the length parameter
+ * received on the response.
+ */
+int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
+   enum query_opcode opcode, enum desc_idn idn, u8 index,
+   u8 selector, u8 *desc_buf, int *buf_len)
+{
+   int err;
+   int retries;
+
+   for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
+   err = __ufshcd_query_descriptor(hba, opcode, idn, index,
+   selector, desc_buf, buf_len);
+   if (!err || err == -EINVAL)
+   break;
+   }
+
+   return err;
+}
+EXPORT_SYMBOL(ufshcd_query_descriptor_retry);
+
+/**
  * ufshcd_read_desc_param - read the specified descriptor parameter
  * @hba: Pointer to adapter instance
  * @desc_id: descriptor idn value
@@ -1995,9 +2014,9 @@ static int ufshcd_read_desc_param(struct ufs_hba *hba,
return -ENOMEM;
}
 
-   ret = ufshcd_query_descriptor(hba, UPIU_QUERY_OPCODE_READ_DESC,
- desc_id, desc_index, 0, desc_buf,
- _len);
+   ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC,
+   desc_id, desc_index, 0, desc_buf,
+   _len);
 
if (ret || (buff_len < ufs_query_desc_max_size[desc_id]) ||
(desc_buf[QUERY_DESC_LENGTH_OFFSET] !=
-- 
1.8.5.2

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


[PATCH v6 15/17] scsi: ufs-qcom: set PA_Local_TX_LCC_Enable before link startup

2016-03-06 Thread Yaniv Gardi
Some UFS devices (and may be host) have issues if LCC is
enabled. So we are setting PA_Local_TX_LCC_Enable to 0
before link startup which will make sure that both host
and device TX LCC are disabled once link startup is
completed.

Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs-qcom.c | 27 ---
 drivers/scsi/ufs/unipro.h   |  1 +
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index ed57729..966bacf 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -16,8 +16,8 @@
 #include 
 #include 
 #include 
-
 #include 
+
 #include "ufshcd.h"
 #include "ufshcd-pltfrm.h"
 #include "unipro.h"
@@ -106,9 +106,11 @@ static void ufs_qcom_disable_lane_clks(struct 
ufs_qcom_host *host)
if (!host->is_lane_clks_enabled)
return;
 
-   clk_disable_unprepare(host->tx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->tx_l1_sync_clk);
clk_disable_unprepare(host->tx_l0_sync_clk);
-   clk_disable_unprepare(host->rx_l1_sync_clk);
+   if (host->hba->lanes_per_direction > 1)
+   clk_disable_unprepare(host->rx_l1_sync_clk);
clk_disable_unprepare(host->rx_l0_sync_clk);
 
host->is_lane_clks_enabled = false;
@@ -272,9 +274,8 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B);
 
if (ret) {
-   dev_err(hba->dev,
-   "%s: ufs_qcom_phy_calibrate_phy()failed, ret = %d\n",
-   __func__, ret);
+   dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret 
= %d\n",
+   __func__, ret);
goto out;
}
 
@@ -524,6 +525,18 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba 
*hba,
err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba,
  150);
 
+   /*
+* Some UFS devices (and may be host) have issues if LCC is
+* enabled. So we are setting PA_Local_TX_LCC_Enable to 0
+* before link startup which will make sure that both host
+* and device TX LCC are disabled once link startup is
+* completed.
+*/
+   if (ufshcd_get_local_unipro_ver(hba) != UFS_UNIPRO_VER_1_41)
+   err = ufshcd_dme_set(hba,
+   UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE),
+   0);
+
break;
case POST_CHANGE:
ufs_qcom_link_startup_post_change(hba);
@@ -1542,7 +1555,7 @@ static int ufs_qcom_probe(struct platform_device *pdev)
  * ufs_qcom_remove - set driver_data of the device to NULL
  * @pdev: pointer to platform device handle
  *
- * Always return 0
+ * Always returns 0
  */
 static int ufs_qcom_remove(struct platform_device *pdev)
 {
diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h
index 8082020..e2854e4 100644
--- a/drivers/scsi/ufs/unipro.h
+++ b/drivers/scsi/ufs/unipro.h
@@ -75,6 +75,7 @@
 #define PA_MAXRXSPEEDFAST  0x1541
 #define PA_MAXRXSPEEDSLOW  0x1542
 #define PA_TXLINKSTARTUPHS 0x1544
+#define PA_LOCAL_TX_LCC_ENABLE 0x155E
 #define PA_TXSPEEDFAST 0x1565
 #define PA_TXSPEEDSLOW 0x1566
 #define PA_REMOTEVERINFO   0x15A0
-- 
1.8.5.2

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


[PATCH v6 10/17] scsi: ufs: add retry for query descriptors

2016-03-06 Thread Yaniv Gardi
Query commands have 100ms timeout and it may timeout if they are
issued in parallel to ongoing read/write SCSI commands, this change
adds the retry (max: 10) in case command timeouts.

Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 55 +++
 1 file changed, 37 insertions(+), 18 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index e9c7c91..394054e 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1875,21 +1875,7 @@ static int ufshcd_query_attr_retry(struct ufs_hba *hba,
return ret;
 }
 
-/**
- * ufshcd_query_descriptor - API function for sending descriptor requests
- * hba: per-adapter instance
- * opcode: attribute opcode
- * idn: attribute idn to access
- * index: index field
- * selector: selector field
- * desc_buf: the buffer that contains the descriptor
- * buf_len: length parameter passed to the device
- *
- * Returns 0 for success, non-zero in case of failure.
- * The buf_len parameter will contain, on return, the length parameter
- * received on the response.
- */
-static int ufshcd_query_descriptor(struct ufs_hba *hba,
+static int __ufshcd_query_descriptor(struct ufs_hba *hba,
enum query_opcode opcode, enum desc_idn idn, u8 index,
u8 selector, u8 *desc_buf, int *buf_len)
 {
@@ -1954,6 +1940,39 @@ out:
 }
 
 /**
+ * ufshcd_query_descriptor_retry - API function for sending descriptor
+ * requests
+ * hba: per-adapter instance
+ * opcode: attribute opcode
+ * idn: attribute idn to access
+ * index: index field
+ * selector: selector field
+ * desc_buf: the buffer that contains the descriptor
+ * buf_len: length parameter passed to the device
+ *
+ * Returns 0 for success, non-zero in case of failure.
+ * The buf_len parameter will contain, on return, the length parameter
+ * received on the response.
+ */
+int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
+   enum query_opcode opcode, enum desc_idn idn, u8 index,
+   u8 selector, u8 *desc_buf, int *buf_len)
+{
+   int err;
+   int retries;
+
+   for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
+   err = __ufshcd_query_descriptor(hba, opcode, idn, index,
+   selector, desc_buf, buf_len);
+   if (!err || err == -EINVAL)
+   break;
+   }
+
+   return err;
+}
+EXPORT_SYMBOL(ufshcd_query_descriptor_retry);
+
+/**
  * ufshcd_read_desc_param - read the specified descriptor parameter
  * @hba: Pointer to adapter instance
  * @desc_id: descriptor idn value
@@ -1995,9 +2014,9 @@ static int ufshcd_read_desc_param(struct ufs_hba *hba,
return -ENOMEM;
}
 
-   ret = ufshcd_query_descriptor(hba, UPIU_QUERY_OPCODE_READ_DESC,
- desc_id, desc_index, 0, desc_buf,
- _len);
+   ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC,
+   desc_id, desc_index, 0, desc_buf,
+   _len);
 
if (ret || (buff_len < ufs_query_desc_max_size[desc_id]) ||
(desc_buf[QUERY_DESC_LENGTH_OFFSET] !=
-- 
1.8.5.2

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


[PATCH v6 06/17] scsi: ufs: separate device and host quirks

2016-03-06 Thread Yaniv Gardi
Currently we use the host quirks mechanism in order to
handle both device and host controller quirks.
In order to support various of UFS devices we should separate
handling the device quirks from the host controller's.

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Raviv Shvili <rshv...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/Makefile |   2 +-
 drivers/scsi/ufs/ufs.h|  31 +++
 drivers/scsi/ufs/ufs_quirks.c | 100 ++
 drivers/scsi/ufs/ufs_quirks.h | 124 ++
 drivers/scsi/ufs/ufshcd.c |   2 +
 drivers/scsi/ufs/ufshcd.h |   3 +
 6 files changed, 261 insertions(+), 1 deletion(-)
 create mode 100644 drivers/scsi/ufs/ufs_quirks.c
 create mode 100644 drivers/scsi/ufs/ufs_quirks.h

diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 8303bcc..8570d41 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -1,5 +1,5 @@
 # UFSHCD makefile
 obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
-obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
+obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o ufs_quirks.o
 obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
 obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index aacb235..8ec6356 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -196,6 +196,37 @@ enum unit_desc_param {
UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1  = 0x22,
 };
 
+/* Device descriptor parameters offsets in bytes*/
+enum device_desc_param {
+   DEVICE_DESC_PARAM_LEN   = 0x0,
+   DEVICE_DESC_PARAM_TYPE  = 0x1,
+   DEVICE_DESC_PARAM_DEVICE_TYPE   = 0x2,
+   DEVICE_DESC_PARAM_DEVICE_CLASS  = 0x3,
+   DEVICE_DESC_PARAM_DEVICE_SUB_CLASS  = 0x4,
+   DEVICE_DESC_PARAM_PRTCL = 0x5,
+   DEVICE_DESC_PARAM_NUM_LU= 0x6,
+   DEVICE_DESC_PARAM_NUM_WLU   = 0x7,
+   DEVICE_DESC_PARAM_BOOT_ENBL = 0x8,
+   DEVICE_DESC_PARAM_DESC_ACCSS_ENBL   = 0x9,
+   DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA,
+   DEVICE_DESC_PARAM_HIGH_PR_LUN   = 0xB,
+   DEVICE_DESC_PARAM_SEC_RMV_TYPE  = 0xC,
+   DEVICE_DESC_PARAM_SEC_LU= 0xD,
+   DEVICE_DESC_PARAM_BKOP_TERM_LT  = 0xE,
+   DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF,
+   DEVICE_DESC_PARAM_SPEC_VER  = 0x10,
+   DEVICE_DESC_PARAM_MANF_DATE = 0x12,
+   DEVICE_DESC_PARAM_MANF_NAME = 0x14,
+   DEVICE_DESC_PARAM_PRDCT_NAME= 0x15,
+   DEVICE_DESC_PARAM_SN= 0x16,
+   DEVICE_DESC_PARAM_OEM_ID= 0x17,
+   DEVICE_DESC_PARAM_MANF_ID   = 0x18,
+   DEVICE_DESC_PARAM_UD_OFFSET = 0x1A,
+   DEVICE_DESC_PARAM_UD_LEN= 0x1B,
+   DEVICE_DESC_PARAM_RTT_CAP   = 0x1C,
+   DEVICE_DESC_PARAM_FRQ_RTC   = 0x1D,
+};
+
 /*
  * Logical Unit Write Protect
  * 00h: LU not write protected
diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c
new file mode 100644
index 000..476ed01
--- /dev/null
+++ b/drivers/scsi/ufs/ufs_quirks.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ufshcd.h"
+#include "ufs_quirks.h"
+
+static struct ufs_dev_fix ufs_fixups[] = {
+   /* UFS cards deviations table */
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_NO_FASTAUTO),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
+   UFS_DEVICE_QUIRK_PA_TACTIVATE),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
+   UFS_DEVICE_QUIRK_PA_TACTIVATE),
+
+   END_FIX
+};
+
+static int ufs_get_device_info(struct ufs_hba *hba,
+   struct ufs_device_info *card_data)
+{
+   int err;
+   u8 model_index;
+   u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE + 1] = {0};
+   u8 desc_buf[QUERY_DESC_DEVICE_MAX_SIZE];
+
+   err = ufshcd_read_device_desc(hba, desc_buf,
+ 

[PATCH v6 07/17] scsi: ufs: disable vccq if it's not needed by UFS device

2016-03-06 Thread Yaniv Gardi
Some UFS devices don't require VCCQ rail for device operations hence
this change adds support to recognize such devices and remove vote for
the unused VCCQ rail.

Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs.h|  1 +
 drivers/scsi/ufs/ufshcd.c | 60 +++
 2 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 8ec6356..b291fa6 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -501,6 +501,7 @@ struct ufs_vreg {
struct regulator *reg;
const char *name;
bool enabled;
+   bool unused;
int min_uV;
int max_uV;
int min_uA;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 91a341b..987cf27 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -195,6 +195,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba);
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
 bool skip_ref_clk);
 static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused);
 static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba);
 static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
 static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
@@ -4593,6 +4594,12 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
goto out;
 
ufs_advertise_fixup_device(hba);
+
+   ret = ufshcd_set_vccq_rail_unused(hba,
+   (hba->dev_quirks & UFS_DEVICE_NO_VCCQ) ? true : false);
+   if (ret)
+   goto out;
+
/* UFS device is also active now */
ufshcd_set_ufs_dev_active(hba);
ufshcd_force_reset_auto_bkops(hba);
@@ -4743,13 +4750,24 @@ static int ufshcd_config_vreg_load(struct device *dev, 
struct ufs_vreg *vreg,
 static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg,
+  UFS_VREG_LPM_LOAD_UA);
 }
 
 static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
 }
 
 static int ufshcd_config_vreg(struct device *dev,
@@ -4784,7 +4802,9 @@ static int ufshcd_enable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (vreg->enabled || vreg->unused)
goto out;
 
ret = ufshcd_config_vreg(dev, vreg, true);
@@ -4804,7 +4824,9 @@ static int ufshcd_disable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || !vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (!vreg->enabled || vreg->unused)
goto out;
 
ret = regulator_disable(vreg->reg);
@@ -4910,6 +4932,36 @@ static int ufshcd_init_hba_vreg(struct ufs_hba *hba)
return 0;
 }
 
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused)
+{
+   int ret = 0;
+   struct ufs_vreg_info *info = >vreg_info;
+
+   if (!info)
+   goto out;
+   else if (!info->vccq)
+   goto out;
+
+   if (unused) {
+   /* shut off the rail here */
+   ret = ufshcd_toggle_vreg(hba->dev, info->vccq, false);
+   /*
+* Mark this rail as no longer used, so it doesn't get enabled
+* later by mistake
+*/
+   if (!ret)
+   info->vccq->unused = true;
+   } else {
+   /*
+* rail should have been already enabled hence just make sure
+* that unused flag is cleared.
+*/
+   info->vccq->unused = false;
+   }
+out:
+   return ret;
+}
+
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
bool skip_ref_clk)
 {
-- 
1.8.5.2

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


[PATCH v6 06/17] scsi: ufs: separate device and host quirks

2016-03-06 Thread Yaniv Gardi
Currently we use the host quirks mechanism in order to
handle both device and host controller quirks.
In order to support various of UFS devices we should separate
handling the device quirks from the host controller's.

Reviewed-by: Gilad Broner 
Signed-off-by: Raviv Shvili 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/Makefile |   2 +-
 drivers/scsi/ufs/ufs.h|  31 +++
 drivers/scsi/ufs/ufs_quirks.c | 100 ++
 drivers/scsi/ufs/ufs_quirks.h | 124 ++
 drivers/scsi/ufs/ufshcd.c |   2 +
 drivers/scsi/ufs/ufshcd.h |   3 +
 6 files changed, 261 insertions(+), 1 deletion(-)
 create mode 100644 drivers/scsi/ufs/ufs_quirks.c
 create mode 100644 drivers/scsi/ufs/ufs_quirks.h

diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 8303bcc..8570d41 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -1,5 +1,5 @@
 # UFSHCD makefile
 obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
-obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
+obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o ufs_quirks.o
 obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
 obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index aacb235..8ec6356 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -196,6 +196,37 @@ enum unit_desc_param {
UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1  = 0x22,
 };
 
+/* Device descriptor parameters offsets in bytes*/
+enum device_desc_param {
+   DEVICE_DESC_PARAM_LEN   = 0x0,
+   DEVICE_DESC_PARAM_TYPE  = 0x1,
+   DEVICE_DESC_PARAM_DEVICE_TYPE   = 0x2,
+   DEVICE_DESC_PARAM_DEVICE_CLASS  = 0x3,
+   DEVICE_DESC_PARAM_DEVICE_SUB_CLASS  = 0x4,
+   DEVICE_DESC_PARAM_PRTCL = 0x5,
+   DEVICE_DESC_PARAM_NUM_LU= 0x6,
+   DEVICE_DESC_PARAM_NUM_WLU   = 0x7,
+   DEVICE_DESC_PARAM_BOOT_ENBL = 0x8,
+   DEVICE_DESC_PARAM_DESC_ACCSS_ENBL   = 0x9,
+   DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA,
+   DEVICE_DESC_PARAM_HIGH_PR_LUN   = 0xB,
+   DEVICE_DESC_PARAM_SEC_RMV_TYPE  = 0xC,
+   DEVICE_DESC_PARAM_SEC_LU= 0xD,
+   DEVICE_DESC_PARAM_BKOP_TERM_LT  = 0xE,
+   DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF,
+   DEVICE_DESC_PARAM_SPEC_VER  = 0x10,
+   DEVICE_DESC_PARAM_MANF_DATE = 0x12,
+   DEVICE_DESC_PARAM_MANF_NAME = 0x14,
+   DEVICE_DESC_PARAM_PRDCT_NAME= 0x15,
+   DEVICE_DESC_PARAM_SN= 0x16,
+   DEVICE_DESC_PARAM_OEM_ID= 0x17,
+   DEVICE_DESC_PARAM_MANF_ID   = 0x18,
+   DEVICE_DESC_PARAM_UD_OFFSET = 0x1A,
+   DEVICE_DESC_PARAM_UD_LEN= 0x1B,
+   DEVICE_DESC_PARAM_RTT_CAP   = 0x1C,
+   DEVICE_DESC_PARAM_FRQ_RTC   = 0x1D,
+};
+
 /*
  * Logical Unit Write Protect
  * 00h: LU not write protected
diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c
new file mode 100644
index 000..476ed01
--- /dev/null
+++ b/drivers/scsi/ufs/ufs_quirks.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ufshcd.h"
+#include "ufs_quirks.h"
+
+static struct ufs_dev_fix ufs_fixups[] = {
+   /* UFS cards deviations table */
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
+   UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
+   UFS_DEVICE_NO_FASTAUTO),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
+   UFS_DEVICE_QUIRK_PA_TACTIVATE),
+   UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
+   UFS_DEVICE_QUIRK_PA_TACTIVATE),
+
+   END_FIX
+};
+
+static int ufs_get_device_info(struct ufs_hba *hba,
+   struct ufs_device_info *card_data)
+{
+   int err;
+   u8 model_index;
+   u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE + 1] = {0};
+   u8 desc_buf[QUERY_DESC_DEVICE_MAX_SIZE];
+
+   err = ufshcd_read_device_desc(hba, desc_buf,
+   QUERY_DESC_DEVICE_MAX_SIZE);
+   if (err) {
+   dev_err(hba->dev, 

[PATCH v6 07/17] scsi: ufs: disable vccq if it's not needed by UFS device

2016-03-06 Thread Yaniv Gardi
Some UFS devices don't require VCCQ rail for device operations hence
this change adds support to recognize such devices and remove vote for
the unused VCCQ rail.

Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufs.h|  1 +
 drivers/scsi/ufs/ufshcd.c | 60 +++
 2 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 8ec6356..b291fa6 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -501,6 +501,7 @@ struct ufs_vreg {
struct regulator *reg;
const char *name;
bool enabled;
+   bool unused;
int min_uV;
int max_uV;
int min_uA;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 91a341b..987cf27 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -195,6 +195,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba);
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
 bool skip_ref_clk);
 static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused);
 static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba);
 static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
 static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
@@ -4593,6 +4594,12 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
goto out;
 
ufs_advertise_fixup_device(hba);
+
+   ret = ufshcd_set_vccq_rail_unused(hba,
+   (hba->dev_quirks & UFS_DEVICE_NO_VCCQ) ? true : false);
+   if (ret)
+   goto out;
+
/* UFS device is also active now */
ufshcd_set_ufs_dev_active(hba);
ufshcd_force_reset_auto_bkops(hba);
@@ -4743,13 +4750,24 @@ static int ufshcd_config_vreg_load(struct device *dev, 
struct ufs_vreg *vreg,
 static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg,
+  UFS_VREG_LPM_LOAD_UA);
 }
 
 static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
 struct ufs_vreg *vreg)
 {
-   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
+   if (!vreg)
+   return 0;
+   else if (vreg->unused)
+   return 0;
+   else
+   return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
 }
 
 static int ufshcd_config_vreg(struct device *dev,
@@ -4784,7 +4802,9 @@ static int ufshcd_enable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (vreg->enabled || vreg->unused)
goto out;
 
ret = ufshcd_config_vreg(dev, vreg, true);
@@ -4804,7 +4824,9 @@ static int ufshcd_disable_vreg(struct device *dev, struct 
ufs_vreg *vreg)
 {
int ret = 0;
 
-   if (!vreg || !vreg->enabled)
+   if (!vreg)
+   goto out;
+   else if (!vreg->enabled || vreg->unused)
goto out;
 
ret = regulator_disable(vreg->reg);
@@ -4910,6 +4932,36 @@ static int ufshcd_init_hba_vreg(struct ufs_hba *hba)
return 0;
 }
 
+static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused)
+{
+   int ret = 0;
+   struct ufs_vreg_info *info = >vreg_info;
+
+   if (!info)
+   goto out;
+   else if (!info->vccq)
+   goto out;
+
+   if (unused) {
+   /* shut off the rail here */
+   ret = ufshcd_toggle_vreg(hba->dev, info->vccq, false);
+   /*
+* Mark this rail as no longer used, so it doesn't get enabled
+* later by mistake
+*/
+   if (!ret)
+   info->vccq->unused = true;
+   } else {
+   /*
+* rail should have been already enabled hence just make sure
+* that unused flag is cleared.
+*/
+   info->vccq->unused = false;
+   }
+out:
+   return ret;
+}
+
 static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
bool skip_ref_clk)
 {
-- 
1.8.5.2

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


[PATCH v6 11/17] scsi: ufs: handle non spec compliant bkops behaviour by device

2016-03-06 Thread Yaniv Gardi
We are seeing that some devices are raising the urgent bkops exception
events even when BKOPS status doesn't indicate performace impacted or
critical. Handle these device by determining their urgent bkops status
at runtime.

Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h |  6 ++
 2 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 394054e..d28c629 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3763,7 +3763,7 @@ out:
  */
 static int ufshcd_urgent_bkops(struct ufs_hba *hba)
 {
-   return ufshcd_bkops_ctrl(hba, BKOPS_STATUS_PERF_IMPACT);
+   return ufshcd_bkops_ctrl(hba, hba->urgent_bkops_lvl);
 }
 
 static inline int ufshcd_get_ee_status(struct ufs_hba *hba, u32 *status)
@@ -3772,6 +3772,43 @@ static inline int ufshcd_get_ee_status(struct ufs_hba 
*hba, u32 *status)
QUERY_ATTR_IDN_EE_STATUS, 0, 0, status);
 }
 
+static void ufshcd_bkops_exception_event_handler(struct ufs_hba *hba)
+{
+   int err;
+   u32 curr_status = 0;
+
+   if (hba->is_urgent_bkops_lvl_checked)
+   goto enable_auto_bkops;
+
+   err = ufshcd_get_bkops_status(hba, _status);
+   if (err) {
+   dev_err(hba->dev, "%s: failed to get BKOPS status %d\n",
+   __func__, err);
+   goto out;
+   }
+
+   /*
+* We are seeing that some devices are raising the urgent bkops
+* exception events even when BKOPS status doesn't indicate performace
+* impacted or critical. Handle these device by determining their urgent
+* bkops status at runtime.
+*/
+   if (curr_status < BKOPS_STATUS_PERF_IMPACT) {
+   dev_err(hba->dev, "%s: device raised urgent BKOPS exception for 
bkops status %d\n",
+   __func__, curr_status);
+   /* update the current status as the urgent bkops level */
+   hba->urgent_bkops_lvl = curr_status;
+   hba->is_urgent_bkops_lvl_checked = true;
+   }
+
+enable_auto_bkops:
+   err = ufshcd_enable_auto_bkops(hba);
+out:
+   if (err < 0)
+   dev_err(hba->dev, "%s: failed to handle urgent bkops %d\n",
+   __func__, err);
+}
+
 /**
  * ufshcd_exception_event_handler - handle exceptions raised by device
  * @work: pointer to work data
@@ -3795,12 +3832,10 @@ static void ufshcd_exception_event_handler(struct 
work_struct *work)
}
 
status &= hba->ee_ctrl_mask;
-   if (status & MASK_EE_URGENT_BKOPS) {
-   err = ufshcd_urgent_bkops(hba);
-   if (err < 0)
-   dev_err(hba->dev, "%s: failed to handle urgent bkops 
%d\n",
-   __func__, err);
-   }
+
+   if (status & MASK_EE_URGENT_BKOPS)
+   ufshcd_bkops_exception_event_handler(hba);
+
 out:
pm_runtime_put_sync(hba->dev);
return;
@@ -4763,6 +4798,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
 
ufshcd_init_pwr_info(hba);
 
+   /* set the default level for urgent bkops */
+   hba->urgent_bkops_lvl = BKOPS_STATUS_PERF_IMPACT;
+   hba->is_urgent_bkops_lvl_checked = false;
+
/* UniPro link is active now */
ufshcd_set_link_active(hba);
 
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 77e79c0..65f29aa 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -383,6 +383,9 @@ struct ufs_init_prefetch {
  * @clk_list_head: UFS host controller clocks list node head
  * @pwr_info: holds current power mode
  * @max_pwr_info: keeps the device max valid pwm
+ * @urgent_bkops_lvl: keeps track of urgent bkops level for device
+ * @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for
+ *  device is known or not.
  */
 struct ufs_hba {
void __iomem *mmio_base;
@@ -538,6 +541,9 @@ struct ufs_hba {
struct devfreq *devfreq;
struct ufs_clk_scaling clk_scaling;
bool is_sys_suspended;
+
+   enum bkops_status urgent_bkops_lvl;
+   bool is_urgent_bkops_lvl_checked;
 };
 
 /* Returns true if clocks can be gated. Otherwise false */
-- 
1.8.5.2

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


[PATCH v6 09/17] scsi: ufs: add error recovery after DL NAC error

2016-03-06 Thread Yaniv Gardi
Some vendor's UFS device sends back to back NACs for the DL data frames
causing the host controller to raise the DFES error status. Sometimes
such UFS devices send back to back NAC without waiting for new
retransmitted DL frame from the host and in such cases it might be
possible the Host UniPro goes into bad state without raising the DFES
error interrupt. If this happens then all the pending commands would
timeout only after respective SW command (which is generally too
large).

This change workarounds such device behaviour like this:
- As soon as SW sees the DL NAC error, it would schedule the error
  handler
- Error handler would sleep for 50ms to see if there any fatal errors
  raised by UFS controller.
   - If there are fatal errors then SW does normal error recovery.
   - If there are no fatal errors then SW sends the NOP command to
 device to check if link is alive.
   - If NOP command times out, SW does normal error recovery
   - If NOP command succeed, skip the error handling.

If DL NAC error is seen multiple times with some vendor's UFS devices
then enable this quirk to initiate quick error recovery and also
silence related error logs to reduce spamming of kernel logs.

Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 93 +++
 drivers/scsi/ufs/ufshci.h |  2 +
 2 files changed, 95 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index dc096f1..e9c7c91 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3795,6 +3795,79 @@ static void ufshcd_complete_requests(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_quirk_dl_nac_errors - This function checks if error handling is
+ * to recover from the DL NAC errors or not.
+ * @hba: per-adapter instance
+ *
+ * Returns true if error handling is required, false otherwise
+ */
+static bool ufshcd_quirk_dl_nac_errors(struct ufs_hba *hba)
+{
+   unsigned long flags;
+   bool err_handling = true;
+
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   /*
+* UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS only workaround the
+* device fatal error and/or DL NAC & REPLAY timeout errors.
+*/
+   if (hba->saved_err & (CONTROLLER_FATAL_ERROR | SYSTEM_BUS_FATAL_ERROR))
+   goto out;
+
+   if ((hba->saved_err & DEVICE_FATAL_ERROR) ||
+   ((hba->saved_err & UIC_ERROR) &&
+(hba->saved_uic_err & UFSHCD_UIC_DL_TCx_REPLAY_ERROR)))
+   goto out;
+
+   if ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)) {
+   int err;
+   /*
+* wait for 50ms to see if we can get any other errors or not.
+*/
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   msleep(50);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   /*
+* now check if we have got any other severe errors other than
+* DL NAC error?
+*/
+   if ((hba->saved_err & INT_FATAL_ERRORS) ||
+   ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)))
+   goto out;
+
+   /*
+* As DL NAC is the only error received so far, send out NOP
+* command to confirm if link is still active or not.
+*   - If we don't get any response then do error recovery.
+*   - If we get response then clear the DL NAC error bit.
+*/
+
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   err = ufshcd_verify_dev_init(hba);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   if (err)
+   goto out;
+
+   /* Link seems to be alive hence ignore the DL NAC errors */
+   if (hba->saved_uic_err == UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)
+   hba->saved_err &= ~UIC_ERROR;
+   /* clear NAC error */
+   hba->saved_uic_err &= ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR;
+   if (!hba->saved_uic_err) {
+   err_handling = false;
+   goto out;
+   }
+   }
+out:
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   return err_handling;
+}
+
+/**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
  */
@@ -3822,6 +3895,17 @@ static void ufshcd_err_handler(struct work_struct *work)
 
/* Complete requests that have door-bel

[PATCH v6 11/17] scsi: ufs: handle non spec compliant bkops behaviour by device

2016-03-06 Thread Yaniv Gardi
We are seeing that some devices are raising the urgent bkops exception
events even when BKOPS status doesn't indicate performace impacted or
critical. Handle these device by determining their urgent bkops status
at runtime.

Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 53 ---
 drivers/scsi/ufs/ufshcd.h |  6 ++
 2 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 394054e..d28c629 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3763,7 +3763,7 @@ out:
  */
 static int ufshcd_urgent_bkops(struct ufs_hba *hba)
 {
-   return ufshcd_bkops_ctrl(hba, BKOPS_STATUS_PERF_IMPACT);
+   return ufshcd_bkops_ctrl(hba, hba->urgent_bkops_lvl);
 }
 
 static inline int ufshcd_get_ee_status(struct ufs_hba *hba, u32 *status)
@@ -3772,6 +3772,43 @@ static inline int ufshcd_get_ee_status(struct ufs_hba 
*hba, u32 *status)
QUERY_ATTR_IDN_EE_STATUS, 0, 0, status);
 }
 
+static void ufshcd_bkops_exception_event_handler(struct ufs_hba *hba)
+{
+   int err;
+   u32 curr_status = 0;
+
+   if (hba->is_urgent_bkops_lvl_checked)
+   goto enable_auto_bkops;
+
+   err = ufshcd_get_bkops_status(hba, _status);
+   if (err) {
+   dev_err(hba->dev, "%s: failed to get BKOPS status %d\n",
+   __func__, err);
+   goto out;
+   }
+
+   /*
+* We are seeing that some devices are raising the urgent bkops
+* exception events even when BKOPS status doesn't indicate performace
+* impacted or critical. Handle these device by determining their urgent
+* bkops status at runtime.
+*/
+   if (curr_status < BKOPS_STATUS_PERF_IMPACT) {
+   dev_err(hba->dev, "%s: device raised urgent BKOPS exception for 
bkops status %d\n",
+   __func__, curr_status);
+   /* update the current status as the urgent bkops level */
+   hba->urgent_bkops_lvl = curr_status;
+   hba->is_urgent_bkops_lvl_checked = true;
+   }
+
+enable_auto_bkops:
+   err = ufshcd_enable_auto_bkops(hba);
+out:
+   if (err < 0)
+   dev_err(hba->dev, "%s: failed to handle urgent bkops %d\n",
+   __func__, err);
+}
+
 /**
  * ufshcd_exception_event_handler - handle exceptions raised by device
  * @work: pointer to work data
@@ -3795,12 +3832,10 @@ static void ufshcd_exception_event_handler(struct 
work_struct *work)
}
 
status &= hba->ee_ctrl_mask;
-   if (status & MASK_EE_URGENT_BKOPS) {
-   err = ufshcd_urgent_bkops(hba);
-   if (err < 0)
-   dev_err(hba->dev, "%s: failed to handle urgent bkops 
%d\n",
-   __func__, err);
-   }
+
+   if (status & MASK_EE_URGENT_BKOPS)
+   ufshcd_bkops_exception_event_handler(hba);
+
 out:
pm_runtime_put_sync(hba->dev);
return;
@@ -4763,6 +4798,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
 
ufshcd_init_pwr_info(hba);
 
+   /* set the default level for urgent bkops */
+   hba->urgent_bkops_lvl = BKOPS_STATUS_PERF_IMPACT;
+   hba->is_urgent_bkops_lvl_checked = false;
+
/* UniPro link is active now */
ufshcd_set_link_active(hba);
 
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 77e79c0..65f29aa 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -383,6 +383,9 @@ struct ufs_init_prefetch {
  * @clk_list_head: UFS host controller clocks list node head
  * @pwr_info: holds current power mode
  * @max_pwr_info: keeps the device max valid pwm
+ * @urgent_bkops_lvl: keeps track of urgent bkops level for device
+ * @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for
+ *  device is known or not.
  */
 struct ufs_hba {
void __iomem *mmio_base;
@@ -538,6 +541,9 @@ struct ufs_hba {
struct devfreq *devfreq;
struct ufs_clk_scaling clk_scaling;
bool is_sys_suspended;
+
+   enum bkops_status urgent_bkops_lvl;
+   bool is_urgent_bkops_lvl_checked;
 };
 
 /* Returns true if clocks can be gated. Otherwise false */
-- 
1.8.5.2

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


[PATCH v6 09/17] scsi: ufs: add error recovery after DL NAC error

2016-03-06 Thread Yaniv Gardi
Some vendor's UFS device sends back to back NACs for the DL data frames
causing the host controller to raise the DFES error status. Sometimes
such UFS devices send back to back NAC without waiting for new
retransmitted DL frame from the host and in such cases it might be
possible the Host UniPro goes into bad state without raising the DFES
error interrupt. If this happens then all the pending commands would
timeout only after respective SW command (which is generally too
large).

This change workarounds such device behaviour like this:
- As soon as SW sees the DL NAC error, it would schedule the error
  handler
- Error handler would sleep for 50ms to see if there any fatal errors
  raised by UFS controller.
   - If there are fatal errors then SW does normal error recovery.
   - If there are no fatal errors then SW sends the NOP command to
 device to check if link is alive.
   - If NOP command times out, SW does normal error recovery
   - If NOP command succeed, skip the error handling.

If DL NAC error is seen multiple times with some vendor's UFS devices
then enable this quirk to initiate quick error recovery and also
silence related error logs to reduce spamming of kernel logs.

Signed-off-by: Subhash Jadavani 
Signed-off-by: Yaniv Gardi 

---
 drivers/scsi/ufs/ufshcd.c | 93 +++
 drivers/scsi/ufs/ufshci.h |  2 +
 2 files changed, 95 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index dc096f1..e9c7c91 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3795,6 +3795,79 @@ static void ufshcd_complete_requests(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_quirk_dl_nac_errors - This function checks if error handling is
+ * to recover from the DL NAC errors or not.
+ * @hba: per-adapter instance
+ *
+ * Returns true if error handling is required, false otherwise
+ */
+static bool ufshcd_quirk_dl_nac_errors(struct ufs_hba *hba)
+{
+   unsigned long flags;
+   bool err_handling = true;
+
+   spin_lock_irqsave(hba->host->host_lock, flags);
+   /*
+* UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS only workaround the
+* device fatal error and/or DL NAC & REPLAY timeout errors.
+*/
+   if (hba->saved_err & (CONTROLLER_FATAL_ERROR | SYSTEM_BUS_FATAL_ERROR))
+   goto out;
+
+   if ((hba->saved_err & DEVICE_FATAL_ERROR) ||
+   ((hba->saved_err & UIC_ERROR) &&
+(hba->saved_uic_err & UFSHCD_UIC_DL_TCx_REPLAY_ERROR)))
+   goto out;
+
+   if ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)) {
+   int err;
+   /*
+* wait for 50ms to see if we can get any other errors or not.
+*/
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   msleep(50);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   /*
+* now check if we have got any other severe errors other than
+* DL NAC error?
+*/
+   if ((hba->saved_err & INT_FATAL_ERRORS) ||
+   ((hba->saved_err & UIC_ERROR) &&
+   (hba->saved_uic_err & ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)))
+   goto out;
+
+   /*
+* As DL NAC is the only error received so far, send out NOP
+* command to confirm if link is still active or not.
+*   - If we don't get any response then do error recovery.
+*   - If we get response then clear the DL NAC error bit.
+*/
+
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   err = ufshcd_verify_dev_init(hba);
+   spin_lock_irqsave(hba->host->host_lock, flags);
+
+   if (err)
+   goto out;
+
+   /* Link seems to be alive hence ignore the DL NAC errors */
+   if (hba->saved_uic_err == UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)
+   hba->saved_err &= ~UIC_ERROR;
+   /* clear NAC error */
+   hba->saved_uic_err &= ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR;
+   if (!hba->saved_uic_err) {
+   err_handling = false;
+   goto out;
+   }
+   }
+out:
+   spin_unlock_irqrestore(hba->host->host_lock, flags);
+   return err_handling;
+}
+
+/**
  * ufshcd_err_handler - handle UFS errors that require s/w attention
  * @work: pointer to work structure
  */
@@ -3822,6 +3895,17 @@ static void ufshcd_err_handler(struct work_struct *work)
 
/* Complete requests that have door-bell cleared by h/w */
ufshcd_complete_requests(hba);
+
+

[PATCH v6 17/17] scsi: ufs-qcom: add printouts of testbus debug registers

2016-03-06 Thread Yaniv Gardi
This change adds printouts of testbus and debug registers.

Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs-qcom.c | 77 +
 drivers/scsi/ufs/ufs-qcom.h |  9 ++
 2 files changed, 86 insertions(+)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 849fcf3..3aedf73 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -58,6 +58,12 @@ static void ufs_qcom_dump_regs(struct ufs_hba *hba, int 
offset, int len,
len * 4, false);
 }
 
+static void ufs_qcom_dump_regs_wrapper(struct ufs_hba *hba, int offset, int 
len,
+   char *prefix, void *priv)
+{
+   ufs_qcom_dump_regs(hba, offset, len, prefix);
+}
+
 static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes)
 {
int err = 0;
@@ -1397,6 +1403,74 @@ out:
return err;
 }
 
+static void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba,
+   void *priv, void (*print_fn)(struct ufs_hba *hba,
+   int offset, int num_regs, char *str, void *priv))
+{
+   u32 reg;
+   struct ufs_qcom_host *host;
+
+   if (unlikely(!hba)) {
+   pr_err("%s: hba is NULL\n", __func__);
+   return;
+   }
+   if (unlikely(!print_fn)) {
+   dev_err(hba->dev, "%s: print_fn is NULL\n", __func__);
+   return;
+   }
+
+   host = ufshcd_get_variant(hba);
+   if (!(host->dbg_print_en & UFS_QCOM_DBG_PRINT_REGS_EN))
+   return;
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_REG_OCSC);
+   print_fn(hba, reg, 44, "UFS_UFS_DBG_RD_REG_OCSC ", priv);
+
+   reg = ufshcd_readl(hba, REG_UFS_CFG1);
+   reg |= UFS_BIT(17);
+   ufshcd_writel(hba, reg, REG_UFS_CFG1);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_EDTL_RAM);
+   print_fn(hba, reg, 32, "UFS_UFS_DBG_RD_EDTL_RAM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_DESC_RAM);
+   print_fn(hba, reg, 128, "UFS_UFS_DBG_RD_DESC_RAM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_PRDT_RAM);
+   print_fn(hba, reg, 64, "UFS_UFS_DBG_RD_PRDT_RAM ", priv);
+
+   ufshcd_writel(hba, (reg & ~UFS_BIT(17)), REG_UFS_CFG1);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UAWM);
+   print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UAWM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UARM);
+   print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UARM ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TXUC);
+   print_fn(hba, reg, 48, "UFS_DBG_RD_REG_TXUC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_RXUC);
+   print_fn(hba, reg, 27, "UFS_DBG_RD_REG_RXUC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_DFC);
+   print_fn(hba, reg, 19, "UFS_DBG_RD_REG_DFC ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TRLUT);
+   print_fn(hba, reg, 34, "UFS_DBG_RD_REG_TRLUT ", priv);
+
+   reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TMRLUT);
+   print_fn(hba, reg, 9, "UFS_DBG_RD_REG_TMRLUT ", priv);
+}
+
+static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host)
+{
+   if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
+   ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1);
+   else
+   ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1);
+}
+
 static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host)
 {
/* provide a legal default configuration */
@@ -1505,6 +1579,7 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
ufshcd_rmwl(host->hba, mask,
(u32)host->testbus.select_minor << offset,
reg);
+   ufs_qcom_enable_test_bus(host);
ufshcd_release(host->hba);
pm_runtime_put_sync(host->hba->dev);
 
@@ -1521,8 +1596,10 @@ static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16,
"HCI Vendor Specific Registers ");
 
+   ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper);
ufs_qcom_testbus_read(hba);
 }
+
 /**
  * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
  *
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 36249b3..a19307a 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -241,6 +241,15 @@ struct ufs_qcom_host {
struct ufs_qcom_testbus testbus;
 };
 
+static inline u32
+ufs_qcom_get_debug_reg_offset(struct ufs_qcom_host *host, u32 reg)
+{
+

[PATCH v6 12/17] scsi: ufs: tune UniPro parameters to optimize hibern8 exit time

2016-03-06 Thread Yaniv Gardi
Optimal values of local UniPro parameters like PA_Hibern8Time &
PA_TActivate can help reduce the hibern8 exit latency. If both host and
device supports UniPro ver1.6 or later, these parameters will be
automatically tuned during link startup itself. But if either host or
device doesn't support UniPro ver 1.6 or later, we have to manually
tune them. But to keep manual tuning logic simple, we will only do
manual tuning if local unipro version doesn't support ver1.6 or later.

Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 118 ++
 drivers/scsi/ufs/ufshcd.h |   1 +
 drivers/scsi/ufs/ufshci.h |   2 +
 drivers/scsi/ufs/unipro.h |  21 +
 4 files changed, 142 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index d28c629..c9c4d68 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -570,6 +570,34 @@ static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
 }
 
+u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba)
+{
+   /* HCI version 1.0 and 1.1 supports UniPro 1.41 */
+   if ((hba->ufs_version == UFSHCI_VERSION_10) ||
+   (hba->ufs_version == UFSHCI_VERSION_11))
+   return UFS_UNIPRO_VER_1_41;
+   else
+   return UFS_UNIPRO_VER_1_6;
+}
+EXPORT_SYMBOL(ufshcd_get_local_unipro_ver);
+
+static bool ufshcd_is_unipro_pa_params_tuning_req(struct ufs_hba *hba)
+{
+   /*
+* If both host and device support UniPro ver1.6 or later, PA layer
+* parameters tuning happens during link startup itself.
+*
+* We can manually tune PA layer parameters if either host or device
+* doesn't support UniPro ver 1.6 or later. But to keep manual tuning
+* logic simple, we will only do manual tuning if local unipro version
+* doesn't support ver1.6 or later.
+*/
+   if (ufshcd_get_local_unipro_ver(hba) < UFS_UNIPRO_VER_1_6)
+   return true;
+   else
+   return false;
+}
+
 static void ufshcd_ungate_work(struct work_struct *work)
 {
int ret;
@@ -4783,6 +4811,95 @@ out:
 }
 
 /**
+ * ufshcd_tune_pa_tactivate - Tunes PA_TActivate of local UniPro
+ * @hba: per-adapter instance
+ *
+ * PA_TActivate parameter can be tuned manually if UniPro version is less than
+ * 1.61. PA_TActivate needs to be greater than or equal to peerM-PHY's
+ * RX_MIN_ACTIVATETIME_CAPABILITY attribute. This optimal value can help reduce
+ * the hibern8 exit latency.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_tune_pa_tactivate(struct ufs_hba *hba)
+{
+   int ret = 0;
+   u32 peer_rx_min_activatetime = 0, tuned_pa_tactivate;
+
+   ret = ufshcd_dme_peer_get(hba,
+ UIC_ARG_MIB_SEL(
+   RX_MIN_ACTIVATETIME_CAPABILITY,
+   UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
+ _rx_min_activatetime);
+   if (ret)
+   goto out;
+
+   /* make sure proper unit conversion is applied */
+   tuned_pa_tactivate =
+   ((peer_rx_min_activatetime * RX_MIN_ACTIVATETIME_UNIT_US)
+/ PA_TACTIVATE_TIME_UNIT_US);
+   ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE),
+tuned_pa_tactivate);
+
+out:
+   return ret;
+}
+
+/**
+ * ufshcd_tune_pa_hibern8time - Tunes PA_Hibern8Time of local UniPro
+ * @hba: per-adapter instance
+ *
+ * PA_Hibern8Time parameter can be tuned manually if UniPro version is less 
than
+ * 1.61. PA_Hibern8Time needs to be maximum of local M-PHY's
+ * TX_HIBERN8TIME_CAPABILITY & peer M-PHY's RX_HIBERN8TIME_CAPABILITY.
+ * This optimal value can help reduce the hibern8 exit latency.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_tune_pa_hibern8time(struct ufs_hba *hba)
+{
+   int ret = 0;
+   u32 local_tx_hibern8_time_cap = 0, peer_rx_hibern8_time_cap = 0;
+   u32 max_hibern8_time, tuned_pa_hibern8time;
+
+   ret = ufshcd_dme_get(hba,
+UIC_ARG_MIB_SEL(TX_HIBERN8TIME_CAPABILITY,
+   UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
+ _tx_hibern8_time_cap);
+   if (ret)
+   goto out;
+
+   ret = ufshcd_dme_peer_get(hba,
+ UIC_ARG_MIB_SEL(RX_HIBERN8TIME_CAPABILITY,
+   UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
+ _rx_hibern8_time_cap);
+   if (ret)
+   goto out;
+
+   max_hibern8_time = max(local_tx_hibern8_time_cap,
+  peer_rx_hibern8_time_cap);
+   /* make sure 

[PATCH v6 05/17] scsi: ufs: add support to read device and string descriptors

2016-03-06 Thread Yaniv Gardi
This change adds support to read device descriptor and string descriptor
from a UFS device

Reviewed-by: Gilad Broner <gbro...@codeaurora.org>
Signed-off-by: Raviv Shvili <rshv...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs.h|  1 +
 drivers/scsi/ufs/ufshcd.c | 88 ++-
 drivers/scsi/ufs/ufshcd.h |  7 
 3 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 54a16ce..aacb235 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -43,6 +43,7 @@
 #define GENERAL_UPIU_REQUEST_SIZE 32
 #define QUERY_DESC_MAX_SIZE   255
 #define QUERY_DESC_MIN_SIZE   2
+#define QUERY_DESC_HDR_SIZE   2
 #define QUERY_OSF_SIZE(GENERAL_UPIU_REQUEST_SIZE - \
(sizeof(struct utp_upiu_header)))
 
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 80031e6..e2ed415 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -39,7 +39,7 @@
 
 #include 
 #include 
-
+#include 
 #include 
 #include "ufshcd.h"
 #include "unipro.h"
@@ -232,6 +232,16 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
}
 }
 
+/* replace non-printable or non-ASCII characters with spaces */
+static inline void ufshcd_remove_non_printable(char *val)
+{
+   if (!val)
+   return;
+
+   if (*val < 0x20 || *val > 0x7e)
+   *val = ' ';
+}
+
 /*
  * ufshcd_wait_for_register - wait for register value to change
  * @hba - per-adapter interface
@@ -2021,6 +2031,82 @@ static inline int ufshcd_read_power_desc(struct ufs_hba 
*hba,
return ufshcd_read_desc(hba, QUERY_DESC_IDN_POWER, 0, buf, size);
 }
 
+int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size)
+{
+   return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, buf, size);
+}
+EXPORT_SYMBOL(ufshcd_read_device_desc);
+
+/**
+ * ufshcd_read_string_desc - read string descriptor
+ * @hba: pointer to adapter instance
+ * @desc_index: descriptor index
+ * @buf: pointer to buffer where descriptor would be read
+ * @size: size of buf
+ * @ascii: if true convert from unicode to ascii characters
+ *
+ * Return 0 in case of success, non-zero otherwise
+ */
+int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
+   u32 size, bool ascii)
+{
+   int err = 0;
+
+   err = ufshcd_read_desc(hba,
+   QUERY_DESC_IDN_STRING, desc_index, buf, size);
+
+   if (err) {
+   dev_err(hba->dev, "%s: reading String Desc failed after %d 
retries. err = %d\n",
+   __func__, QUERY_REQ_RETRIES, err);
+   goto out;
+   }
+
+   if (ascii) {
+   int desc_len;
+   int ascii_len;
+   int i;
+   char *buff_ascii;
+
+   desc_len = buf[0];
+   /* remove header and divide by 2 to move from UTF16 to UTF8 */
+   ascii_len = (desc_len - QUERY_DESC_HDR_SIZE) / 2 + 1;
+   if (size < ascii_len + QUERY_DESC_HDR_SIZE) {
+   dev_err(hba->dev, "%s: buffer allocated size is too 
small\n",
+   __func__);
+   err = -ENOMEM;
+   goto out;
+   }
+
+   buff_ascii = kmalloc(ascii_len, GFP_KERNEL);
+   if (!buff_ascii) {
+   err = -ENOMEM;
+   goto out_free_buff;
+   }
+
+   /*
+* the descriptor contains string in UTF16 format
+* we need to convert to utf-8 so it can be displayed
+*/
+   utf16s_to_utf8s((wchar_t *)[QUERY_DESC_HDR_SIZE],
+   desc_len - QUERY_DESC_HDR_SIZE,
+   UTF16_BIG_ENDIAN, buff_ascii, ascii_len);
+
+   /* replace non-printable or non-ASCII characters with spaces */
+   for (i = 0; i < ascii_len; i++)
+   ufshcd_remove_non_printable(_ascii[i]);
+
+   memset(buf + QUERY_DESC_HDR_SIZE, 0,
+   size - QUERY_DESC_HDR_SIZE);
+   memcpy(buf + QUERY_DESC_HDR_SIZE, buff_ascii, ascii_len);
+   buf[QUERY_DESC_LENGTH_OFFSET] = ascii_len + QUERY_DESC_HDR_SIZE;
+out_free_buff:
+   kfree(buff_ascii);
+   }
+out:
+   return err;
+}
+EXPORT_SYMBOL(ufshcd_read_string_desc);
+
 /**
  * ufshcd_read_unit_desc_param - read the specified unit descriptor parameter
  * @hba: Pointer to adapter instance
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index a6d3572..54e13cc 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -678,6 +678,13 @@ static inline int ufs

[PATCH v6 02/17] scsi: ufs: avoid spurious UFS host controller interrupts

2016-03-06 Thread Yaniv Gardi
When control reaches to Linux UFS driver during UFS boot mode, UFS host
controller interrupt status/enable registers may have left over
settings.
In order to avoid any spurious interrupts due to these left overs,
it's important to clear these interrupt status/enable registers before
enabling UFS interrupt handling.

Signed-off-by: Subhash Jadavani <subha...@codeaurora.org>
Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufshcd.c | 15 +++
 1 file changed, 15 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a8e42df..de7280c 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5837,6 +5837,21 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem 
*mmio_base, unsigned int irq)
init_waitqueue_head(>dev_cmd.tag_wq);
 
ufshcd_init_clk_gating(hba);
+
+   /*
+* In order to avoid any spurious interrupt immediately after
+* registering UFS controller interrupt handler, clear any pending UFS
+* interrupt status and disable all the UFS interrupts.
+*/
+   ufshcd_writel(hba, ufshcd_readl(hba, REG_INTERRUPT_STATUS),
+ REG_INTERRUPT_STATUS);
+   ufshcd_writel(hba, 0, REG_INTERRUPT_ENABLE);
+   /*
+* Make sure that UFS interrupts are disabled and any pending interrupt
+* status is cleared before registering UFS interrupt handler.
+*/
+   mb();
+
/* IRQ registration */
err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
if (err) {
-- 
1.8.5.2

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


  1   2   3   4   5   6   7   8   9   >