diff options
| author | Subhash Jadavani <subhashj@codeaurora.org> | 2015-12-23 12:29:26 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:00:50 -0700 |
| commit | 6de2bbe87eae57fdac702af842145d9d5677eafd (patch) | |
| tree | 2646a63c3224677c8d4cfa23455b8984b7f7cdf4 | |
| parent | 8f47acddef57a6cf29c0f74df08e5f224930e6e2 (diff) | |
scsi: ufs: add quirk to increase host PA_SaveConfigTime
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.
Change-Id: I02a0394bb07f165ba8ae6c5802567cc6d8ad0d16
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
| -rw-r--r-- | drivers/scsi/ufs/ufs-qcom.c | 30 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufs-qcom.h | 1 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufs_quirks.c | 2 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufs_quirks.h | 10 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 2 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 8 |
6 files changed, 53 insertions, 0 deletions
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 11ed15bebb8f..5fe557a48ab4 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -26,6 +26,7 @@ #include "unipro.h" #include "ufs-qcom.h" #include "ufshci.h" +#include "ufs_quirks.h" #include "ufs-qcom-ice.h" #include "ufs-qcom-debugfs.h" #include <linux/clk/msm-clk.h> @@ -1161,6 +1162,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); @@ -2258,6 +2287,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, .full_reset = ufs_qcom_full_reset, diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h index 9db2c327e4b7..1a077c83d5da 100644 --- a/drivers/scsi/ufs/ufs-qcom.h +++ b/drivers/scsi/ufs/ufs-qcom.h @@ -147,6 +147,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) diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c index 689344e1e6ef..176c3888aa34 100644 --- a/drivers/scsi/ufs/ufs_quirks.c +++ b/drivers/scsi/ufs/ufs_quirks.c @@ -28,6 +28,8 @@ static struct ufs_card_fix ufs_fixups[] = { UFS_DEVICE_QUIRK_PA_TACTIVATE), UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE), + UFS_FIX(UFS_VENDOR_HYNIX, UFS_ANY_MODEL, + UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME), END_FIX }; diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h index 8960c8f2d699..b8ab5948f12d 100644 --- a/drivers/scsi/ufs/ufs_quirks.h +++ b/drivers/scsi/ufs/ufs_quirks.h @@ -24,6 +24,7 @@ #define UFS_VENDOR_TOSHIBA 0x198 #define UFS_VENDOR_SAMSUNG 0x1CE +#define UFS_VENDOR_HYNIX 0x1AD /* UFS TOSHIBA MODELS */ #define UFS_MODEL_TOSHIBA_32GB "THGLF2G8D4KBADR" @@ -128,6 +129,15 @@ struct ufs_card_fix { */ #define UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE (1 << 6) +/* + * 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 << 7) struct ufs_hba; void ufs_advertise_fixup_device(struct ufs_hba *hba); diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 9a1e1a6eda80..ae9e5367c05e 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -6617,6 +6617,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); } static void ufshcd_clear_dbg_ufs_stats(struct ufs_hba *hba) diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 5f94b723b708..191be3b0af82 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -325,6 +325,7 @@ struct ufs_hba_variant_ops { enum ufs_notify_change_status status, struct ufs_pa_layer_attr *, struct ufs_pa_layer_attr *); + 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); int (*full_reset)(struct ufs_hba *); @@ -1156,6 +1157,13 @@ static inline int ufshcd_vops_pwr_change_notify(struct ufs_hba *hba, return -ENOTSUPP; } +static inline int ufshcd_vops_apply_dev_quirks(struct ufs_hba *hba) +{ + if (hba->var && hba->var->vops && hba->var->vops->apply_dev_quirks) + return hba->var->vops->apply_dev_quirks(hba); + return 0; +} + static inline int ufshcd_vops_suspend(struct ufs_hba *hba, enum ufs_pm_op op) { if (hba->var && hba->var->vops && hba->var->vops->suspend) |
