Some UFS host controllers may not report a result of
UIC command completion properly.
In such cases, they should get the result from UIC directly
if their architectures support that.

Signed-off-by: Kiwoong Kim <kwmad....@samsung.com>
---
 drivers/scsi/ufs/ufshcd.c | 27 +++++++++++++++++++++++----
 drivers/scsi/ufs/ufshcd.h | 20 ++++++++++++++++++++
 2 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index d4a5a9c..8e19631 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1011,7 +1011,10 @@ static inline bool ufshcd_ready_for_uic_cmd(struct 
ufs_hba *hba)
  */
 static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
 {
-       return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
+       if (hba->quirks & UFSHCD_QUIRK_GET_VS_RESULT)
+               return (u8)ufshcd_vops_get_vs_info(hba, VS_OP_03);
+       else
+               return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
 }
 
 /**
@@ -1038,6 +1041,17 @@ ufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct 
uic_command *uic_cmd)
                      REG_UIC_COMMAND);
 }
 
+static inline enum vs_opcode ufshcd_get_vs_opcode(struct uic_command *uic_cmd)
+{
+       if (uic_cmd->command == UIC_CMD_DME_LINK_STARTUP)
+               return VS_OP_00;
+       else if ((uic_cmd->command == UIC_CMD_DME_HIBER_ENTER))
+               return VS_OP_01;
+       else if ((uic_cmd->command == UIC_CMD_DME_HIBER_EXIT))
+               return VS_OP_02;
+       else
+               return VS_OP_INVALID;
+}
 /**
  * ufshcd_wait_for_uic_cmd - Wait complectioin of UIC command
  * @hba: per adapter instance
@@ -1051,11 +1065,16 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct 
uic_command *uic_cmd)
 {
        int ret;
        unsigned long flags;
+       enum vs_opcode vs_op = ufshcd_get_vs_opcode(uic_cmd);
 
        if (wait_for_completion_timeout(&uic_cmd->done,
-                                       msecs_to_jiffies(UIC_CMD_TIMEOUT)))
-               ret = uic_cmd->argument2 & MASK_UIC_COMMAND_RESULT;
-       else
+                                       msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
+               if (hba->quirks & UFSHCD_QUIRK_GET_VS_RESULT &&
+                                       vs_op != VS_OP_INVALID)
+                       ret = ufshcd_vops_get_vs_info(hba, vs_op);
+               else
+                       ret = uic_cmd->argument2 & MASK_UIC_COMMAND_RESULT;
+       } else
                ret = -ETIMEDOUT;
 
        spin_lock_irqsave(hba->host->host_lock, flags);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 13504b4..8cf3991 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -245,6 +245,14 @@ struct ufs_pwr_mode_info {
        struct ufs_pa_layer_attr info;
 };
 
+enum vs_opcode {
+       VS_OP_00 = 0x00,
+       VS_OP_01,
+       VS_OP_02,
+       VS_OP_03,
+       VS_OP_INVALID = 0xFF,
+};
+
 /**
  * struct ufs_hba_variant_ops - variant specific callbacks
  * @name: variant name
@@ -297,6 +305,7 @@ struct ufs_hba_variant_ops {
        int     (*resume)(struct ufs_hba *, enum ufs_pm_op);
        void    (*dbg_register_dump)(struct ufs_hba *hba);
        int     (*phy_initialization)(struct ufs_hba *);
+       int     (*get_vs_info)(struct ufs_hba *hba, enum vs_opcode);
 };
 
 /* clock gating state  */
@@ -484,6 +493,8 @@ struct ufs_hba {
         */
        #define UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION             UFS_BIT(5)
 
+       #define UFSHCD_QUIRK_GET_VS_RESULT                      UFS_BIT(6)
+
        unsigned int quirks;    /* Deviations from standard UFSHCI spec. */
 
        /* Device deviations from standard UFS device spec. */
@@ -853,4 +864,13 @@ static inline void ufshcd_vops_dbg_register_dump(struct 
ufs_hba *hba)
                hba->vops->dbg_register_dump(hba);
 }
 
+static inline int ufshcd_vops_get_vs_info(struct ufs_hba *hba,
+                                       enum vs_opcode op)
+{
+       if (hba->vops && hba->vops->get_vs_info)
+               return hba->vops->get_vs_info(hba, op);
+
+       return -ENOTSUPP;
+}
+
 #endif /* End of Header */
-- 
2.1.4

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

Reply via email to