scsi: ufs: add quirk to increase host PA_SaveConfigTime
authorSubhash Jadavani <subhashj@codeaurora.org>
Tue, 6 Dec 2016 03:25:32 +0000 (19:25 -0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 8 Dec 2016 22:00:11 +0000 (17:00 -0500)
The maximum value PA_SaveConfigTime is 250 (10us) but this is not enough
for some vendors. Gear switch from PWM to HS may fail even with this
max.  PA_SaveConfigTime. Gear switch can be issued by host controller as
an error recovery and any software delay will not help on this case so
we need to increase PA_SaveConfigTime to >32us as per vendor
recommendation.  This change adds a quirk to increase the
PA_SaveConfigTime parameter.

Reviewed-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/ufs/ufs-qcom.c
drivers/scsi/ufs/ufs-qcom.h
drivers/scsi/ufs/ufs_quirks.h
drivers/scsi/ufs/ufshcd.c
drivers/scsi/ufs/ufshcd.h

index ff614613bf8983b06f79f924fdebed075db22ec8..2943a68b6d69b5c1efe70f14a465906eb58b9075 100644 (file)
@@ -23,6 +23,7 @@
 #include "unipro.h"
 #include "ufs-qcom.h"
 #include "ufshci.h"
+#include "ufs_quirks.h"
 #define UFS_QCOM_DEFAULT_DBG_PRINT_EN  \
        (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
 
@@ -1031,6 +1032,34 @@ out:
        return ret;
 }
 
+static int ufs_qcom_quirk_host_pa_saveconfigtime(struct ufs_hba *hba)
+{
+       int err;
+       u32 pa_vs_config_reg1;
+
+       err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1),
+                            &pa_vs_config_reg1);
+       if (err)
+               goto out;
+
+       /* Allow extension of MSB bits of PA_SaveConfigTime attribute */
+       err = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1),
+                           (pa_vs_config_reg1 | (1 << 12)));
+
+out:
+       return err;
+}
+
+static int ufs_qcom_apply_dev_quirks(struct ufs_hba *hba)
+{
+       int err = 0;
+
+       if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME)
+               err = ufs_qcom_quirk_host_pa_saveconfigtime(hba);
+
+       return err;
+}
+
 static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba)
 {
        struct ufs_qcom_host *host = ufshcd_get_variant(hba);
@@ -1618,6 +1647,7 @@ static struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
        .hce_enable_notify      = ufs_qcom_hce_enable_notify,
        .link_startup_notify    = ufs_qcom_link_startup_notify,
        .pwr_change_notify      = ufs_qcom_pwr_change_notify,
+       .apply_dev_quirks       = ufs_qcom_apply_dev_quirks,
        .suspend                = ufs_qcom_suspend,
        .resume                 = ufs_qcom_resume,
        .dbg_register_dump      = ufs_qcom_dump_dbg_regs,
index a19307a57ce248f5cb102c294408227b8ed603de..fe517cd7dac348b40b97c322e49e26976b7256cc 100644 (file)
@@ -142,6 +142,7 @@ enum ufs_qcom_phy_init_type {
         UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
 
 /* QUniPro Vendor specific attributes */
+#define PA_VS_CONFIG_REG1      0x9000
 #define DME_VS_CORE_CLK_CTRL   0xD002
 /* bit and mask definitions for DME_VS_CORE_CLK_CTRL attribute */
 #define DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT               BIT(8)
index f7983058f3f716ed27505fbcef0d420eaf8a6163..08b799d4efcc68b99b7c43fa7af968dd3b543a2c 100644 (file)
@@ -134,29 +134,17 @@ struct ufs_dev_fix {
  */
 #define UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE     (1 << 7)
 
+/*
+ * The max. value PA_SaveConfigTime is 250 (10us) but this is not enough for
+ * some vendors.
+ * Gear switch from PWM to HS may fail even with this max. PA_SaveConfigTime.
+ * Gear switch can be issued by host controller as an error recovery and any
+ * software delay will not help on this case so we need to increase
+ * PA_SaveConfigTime to >32us as per vendor recommendation.
+ */
+#define UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME        (1 << 8)
 
 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_SAMSUNG, UFS_ANY_MODEL,
-               UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE),
-       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",
-               UFS_DEVICE_QUIRK_PA_TACTIVATE),
-       UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
-
-       END_FIX
-};
 #endif /* UFS_QUIRKS_H_ */
index 7af70aa0b75008f4fdaede828378dada92be56b4..af7e0285f00423a78b4ab6f99e967b6e9ce6a5e5 100644 (file)
@@ -185,6 +185,30 @@ ufs_get_pm_lvl_to_link_pwr_state(enum ufs_pm_level lvl)
        return ufs_pm_lvl_states[lvl].link_state;
 }
 
+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_SAMSUNG, UFS_ANY_MODEL,
+               UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE),
+       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",
+               UFS_DEVICE_QUIRK_PA_TACTIVATE),
+       UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
+       UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL,
+               UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME),
+
+       END_FIX
+};
+
 static void ufshcd_tmc_handler(struct ufs_hba *hba);
 static void ufshcd_async_scan(void *data, async_cookie_t cookie);
 static int ufshcd_reset_and_restore(struct ufs_hba *hba);
@@ -5213,6 +5237,8 @@ static void ufshcd_tune_unipro_params(struct ufs_hba *hba)
 
        if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE)
                ufshcd_quirk_tune_host_pa_tactivate(hba);
+
+       ufshcd_vops_apply_dev_quirks(hba);
 }
 
 /**
index 7d9ff22acfeaf78c29c1ed4e3d95144d2fa18d2c..08cd26ed238270a3f6bfb797137f4962bfe04312 100644 (file)
@@ -266,7 +266,7 @@ struct ufs_pwr_mode_info {
  * @setup_task_mgmt: called before any task management request is issued
  *                  to set some things
  * @hibern8_notify: called around hibern8 enter/exit
- *                 to configure some things
+ * @apply_dev_quirks: called to apply device specific quirks
  * @suspend: called during host controller PM callback
  * @resume: called during host controller PM callback
  * @dbg_register_dump: used to dump controller debug information
@@ -293,7 +293,8 @@ struct ufs_hba_variant_ops {
        void    (*setup_xfer_req)(struct ufs_hba *, int, bool);
        void    (*setup_task_mgmt)(struct ufs_hba *, int, u8);
        void    (*hibern8_notify)(struct ufs_hba *, enum uic_cmd_dme,
-                                      enum ufs_notify_change_status);
+                                       enum ufs_notify_change_status);
+       int     (*apply_dev_quirks)(struct ufs_hba *);
        int     (*suspend)(struct ufs_hba *, enum ufs_pm_op);
        int     (*resume)(struct ufs_hba *, enum ufs_pm_op);
        void    (*dbg_register_dump)(struct ufs_hba *hba);
@@ -839,6 +840,13 @@ static inline void ufshcd_vops_hibern8_notify(struct ufs_hba *hba,
                return hba->vops->hibern8_notify(hba, cmd, status);
 }
 
+static inline int ufshcd_vops_apply_dev_quirks(struct ufs_hba *hba)
+{
+       if (hba->vops && hba->vops->apply_dev_quirks)
+               return hba->vops->apply_dev_quirks(hba);
+       return 0;
+}
+
 static inline int ufshcd_vops_suspend(struct ufs_hba *hba, enum ufs_pm_op op)
 {
        if (hba->vops && hba->vops->suspend)