| From 4a54eccc93c7394af236cb82dd4eeda1214cd7a3 Mon Sep 17 00:00:00 2001 |
| From: Subhash Jadavani <subhashj@codeaurora.org> |
| Date: Mon, 5 Dec 2016 19:25:32 -0800 |
| Subject: [PATCH] scsi: ufs: add quirk to increase host PA_SaveConfigTime |
| |
| commit 56d4a1866d748732fd8d690b2c2156bbc9c9eb02 upstream. |
| |
| 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> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c |
| index 3aedf73f1131..462bf42dd19c 100644 |
| --- a/drivers/scsi/ufs/ufs-qcom.c |
| +++ b/drivers/scsi/ufs/ufs-qcom.c |
| @@ -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); |
| @@ -1616,6 +1645,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, |
| diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h |
| index a19307a57ce2..fe517cd7dac3 100644 |
| --- a/drivers/scsi/ufs/ufs-qcom.h |
| +++ b/drivers/scsi/ufs/ufs-qcom.h |
| @@ -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) |
| diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h |
| index f7983058f3f7..08b799d4efcc 100644 |
| --- a/drivers/scsi/ufs/ufs_quirks.h |
| +++ b/drivers/scsi/ufs/ufs_quirks.h |
| @@ -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_ */ |
| diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c |
| index b2440abf4a23..bf4498f7900e 100644 |
| --- a/drivers/scsi/ufs/ufshcd.c |
| +++ b/drivers/scsi/ufs/ufshcd.c |
| @@ -188,6 +188,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); |
| @@ -5059,6 +5083,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); |
| } |
| |
| /** |
| diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h |
| index 5b65441110fa..04509827fe64 100644 |
| --- a/drivers/scsi/ufs/ufshcd.h |
| +++ b/drivers/scsi/ufs/ufshcd.h |
| @@ -261,6 +261,7 @@ struct ufs_pwr_mode_info { |
| * @pwr_change_notify: called before and after a power mode change |
| * is carried out to allow vendor spesific capabilities |
| * to be set. |
| + * @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 |
| @@ -283,6 +284,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); |
| void (*dbg_register_dump)(struct ufs_hba *hba); |
| @@ -805,6 +807,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->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) |
| -- |
| 2.12.0 |
| |