OSDN Git Service

scsi: ufs: scale up the gear in 2 steps
authorSubhash Jadavani <subhashj@codeaurora.org>
Thu, 23 Mar 2017 19:55:25 +0000 (12:55 -0700)
committerSubhash Jadavani <subhashj@codeaurora.org>
Mon, 3 Apr 2017 17:30:43 +0000 (10:30 -0700)
Some UFS devices may stop responding after switching from HS-G1 to HS-G3.
Also, it is found that these devices work fine if we do 2 steps switch:
HS-G1 to HS-G2 followed by HS-G2 to HS-G3.
This change introduce UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH quirk for
these devices to apply this 2 steps gear switch workaround.

Change-Id: I38eec2e1bfa842169cbfec441d900a807e715f8c
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
drivers/scsi/ufs/ufs_quirks.c
drivers/scsi/ufs/ufs_quirks.h
drivers/scsi/ufs/ufshcd.c

index 176c388..7a501d6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017, 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
@@ -30,6 +30,20 @@ static struct ufs_card_fix ufs_fixups[] = {
                UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE),
        UFS_FIX(UFS_VENDOR_HYNIX, UFS_ANY_MODEL,
                UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME),
+       UFS_FIX(UFS_VENDOR_HYNIX, "hB8aL1",
+               UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH),
+       UFS_FIX(UFS_VENDOR_HYNIX, "hC8aL1",
+               UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH),
+       UFS_FIX(UFS_VENDOR_HYNIX, "hD8aL1",
+               UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH),
+       UFS_FIX(UFS_VENDOR_HYNIX, "hC8aM1",
+               UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH),
+       UFS_FIX(UFS_VENDOR_HYNIX, "h08aM1",
+               UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH),
+       UFS_FIX(UFS_VENDOR_HYNIX, "hC8GL1",
+               UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH),
+       UFS_FIX(UFS_VENDOR_HYNIX, "hC8HL1",
+               UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH),
 
        END_FIX
 };
index b8ab594..3102517 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, 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
@@ -139,6 +139,14 @@ struct ufs_card_fix {
  */
 #define UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME        (1 << 7)
 
+/*
+ * Some UFS devices may stop responding after switching from HS-G1 to HS-G3.
+ * Also, it is found that these devices work fine if we do 2 steps switch:
+ * HS-G1 to HS-G2 followed by HS-G2 to HS-G3. Enabling this quirk for such
+ * device would apply this 2 steps gear switch workaround.
+ */
+#define UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH (1 << 8)
+
 struct ufs_hba;
 void ufs_advertise_fixup_device(struct ufs_hba *hba);
 #endif /* UFS_QUIRKS_H_ */
index 2c46fd6..a02c8a2 100644 (file)
@@ -509,7 +509,7 @@ out:
 /* replace non-printable or non-ASCII characters with spaces */
 static inline void ufshcd_remove_non_printable(char *val)
 {
-       if (!val)
+       if (!val || !*val)
                return;
 
        if (*val < 0x20 || *val > 0x7e)
@@ -3651,7 +3651,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
                        goto out;
                }
 
-               buff_ascii = kmalloc(ascii_len, GFP_KERNEL);
+               buff_ascii = kzalloc(ascii_len, GFP_KERNEL);
                if (!buff_ascii) {
                        dev_err(hba->dev, "%s: Failed allocating %d bytes\n",
                                        __func__, ascii_len);
@@ -9207,6 +9207,31 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up)
        if (scale_up) {
                memcpy(&new_pwr_info, &hba->clk_scaling.saved_pwr_info.info,
                       sizeof(struct ufs_pa_layer_attr));
+               /*
+                * Some UFS devices may stop responding after switching from
+                * HS-G1 to HS-G3. Also, it is found that these devices work
+                * fine if we do 2 steps switch: HS-G1 to HS-G2 followed by
+                * HS-G2 to HS-G3. If UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH
+                * quirk is enabled for such devices, this 2 steps gear switch
+                * workaround will be applied.
+                */
+               if ((hba->dev_quirks & UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH)
+                   && (hba->pwr_info.gear_tx == UFS_HS_G1)
+                   && (new_pwr_info.gear_tx == UFS_HS_G3)) {
+                       /* scale up to G2 first */
+                       new_pwr_info.gear_tx = UFS_HS_G2;
+                       new_pwr_info.gear_rx = UFS_HS_G2;
+                       ret = ufshcd_change_power_mode(hba, &new_pwr_info);
+                       if (ret)
+                               goto out;
+
+                       /* scale up to G3 now */
+                       new_pwr_info.gear_tx = UFS_HS_G3;
+                       new_pwr_info.gear_rx = UFS_HS_G3;
+                       ret = ufshcd_change_power_mode(hba, &new_pwr_info);
+                       if (ret)
+                               goto out;
+               }
        } else {
                memcpy(&new_pwr_info, &hba->pwr_info,
                       sizeof(struct ufs_pa_layer_attr));
@@ -9226,10 +9251,10 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up)
                                new_pwr_info.pwr_rx = FASTAUTO_MODE;
                        }
                }
+               ret = ufshcd_change_power_mode(hba, &new_pwr_info);
        }
 
-       ret = ufshcd_change_power_mode(hba, &new_pwr_info);
-
+out:
        if (ret)
                dev_err(hba->dev, "%s: failed err %d, old gear: (tx %d rx %d), new gear: (tx %d rx %d), scale_up = %d",
                        __func__, ret,