From d0960c5f3000b0a6a79ec4307ba24dff23ff8b1f Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Thu, 23 Mar 2017 12:55:25 -0700 Subject: [PATCH] scsi: ufs: scale up the gear in 2 steps 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 --- drivers/scsi/ufs/ufs_quirks.c | 16 +++++++++++++++- drivers/scsi/ufs/ufs_quirks.h | 10 +++++++++- drivers/scsi/ufs/ufshcd.c | 33 +++++++++++++++++++++++++++++---- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c index 176c3888aa34..7a501d6d7c84 100644 --- a/drivers/scsi/ufs/ufs_quirks.c +++ b/drivers/scsi/ufs/ufs_quirks.c @@ -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 }; diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h index b8ab5948f12d..3102517e841c 100644 --- a/drivers/scsi/ufs/ufs_quirks.h +++ b/drivers/scsi/ufs/ufs_quirks.h @@ -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_ */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 2c46fd61fa47..a02c8a2c756c 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -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, -- 2.11.0