diff options
| author | Subhash Jadavani <subhashj@codeaurora.org> | 2015-11-11 16:52:24 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:00:45 -0700 |
| commit | 8414f23377135c8d4e21ef7a825fcb7afb60e688 (patch) | |
| tree | 74657c8f11151b0ff456337edeba14dc49977d06 /drivers/scsi | |
| parent | b2c36ddc22d2dc68ae27452bf6480baf7cc9b039 (diff) | |
scsi: ufs: ensure that host pa_tactivate is higher than device
Some UFS devices require host PA_TACTIVATE to be higher than
device PA_TACTIVATE otherwise it may get stuck during hibern8 sequence.
This change allows this by using quirk.
Change-Id: Iee1ee7a3e435b34a9e5cca3f5cff3a9b5c36fe1b
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
Diffstat (limited to 'drivers/scsi')
| -rw-r--r-- | drivers/scsi/ufs/ufs_quirks.c | 2 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufs_quirks.h | 7 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 73 | ||||
| -rw-r--r-- | drivers/scsi/ufs/unipro.h | 4 |
4 files changed, 86 insertions, 0 deletions
diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c index aa79554f8ea5..689344e1e6ef 100644 --- a/drivers/scsi/ufs/ufs_quirks.c +++ b/drivers/scsi/ufs/ufs_quirks.c @@ -26,6 +26,8 @@ static struct ufs_card_fix ufs_fixups[] = { UFS_DEVICE_QUIRK_PA_TACTIVATE), UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG", UFS_DEVICE_QUIRK_PA_TACTIVATE), + UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, + UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE), END_FIX }; diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h index 3e014a530a12..8960c8f2d699 100644 --- a/drivers/scsi/ufs/ufs_quirks.h +++ b/drivers/scsi/ufs/ufs_quirks.h @@ -122,6 +122,13 @@ struct ufs_card_fix { */ #define UFS_DEVICE_NO_FASTAUTO (1 << 5) +/* + * Some UFS devices require host PA_TACTIVATE to be lower than device + * PA_TACTIVATE, enabling this quirk ensure this. + */ +#define UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE (1 << 6) + + 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 6c8ceb219ef3..6932a81bb4fa 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -6484,6 +6484,76 @@ out: return ret; } +/** + * ufshcd_quirk_tune_host_pa_tactivate - Ensures that host PA_TACTIVATE is + * more than device PA_TACTIVATE time. + * @hba: per-adapter instance + * + * Some UFS devices require host PA_TACTIVATE to be lower than device + * PA_TACTIVATE, we need to enable UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE quirk + * for such devices. + * + * Returns zero on success, non-zero error value on failure. + */ +static int ufshcd_quirk_tune_host_pa_tactivate(struct ufs_hba *hba) +{ + int ret = 0; + u32 granularity, peer_granularity; + u32 pa_tactivate, peer_pa_tactivate; + u32 pa_tactivate_us, peer_pa_tactivate_us; + u8 gran_to_us_table[] = {1, 4, 8, 16, 32, 100}; + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_GRANULARITY), + &granularity); + if (ret) + goto out; + + ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_GRANULARITY), + &peer_granularity); + if (ret) + goto out; + + if ((granularity < PA_GRANULARITY_MIN_VAL) || + (granularity > PA_GRANULARITY_MAX_VAL)) { + dev_err(hba->dev, "%s: invalid host PA_GRANULARITY %d", + __func__, granularity); + return -EINVAL; + } + + if ((peer_granularity < PA_GRANULARITY_MIN_VAL) || + (peer_granularity > PA_GRANULARITY_MAX_VAL)) { + dev_err(hba->dev, "%s: invalid device PA_GRANULARITY %d", + __func__, peer_granularity); + return -EINVAL; + } + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_TACTIVATE), &pa_tactivate); + if (ret) + goto out; + + ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_TACTIVATE), + &peer_pa_tactivate); + if (ret) + goto out; + + pa_tactivate_us = pa_tactivate * gran_to_us_table[granularity - 1]; + peer_pa_tactivate_us = peer_pa_tactivate * + gran_to_us_table[peer_granularity - 1]; + + if (pa_tactivate_us > peer_pa_tactivate_us) { + u32 new_peer_pa_tactivate; + + new_peer_pa_tactivate = pa_tactivate_us / + gran_to_us_table[peer_granularity - 1]; + new_peer_pa_tactivate++; + ret = ufshcd_dme_peer_set(hba, UIC_ARG_MIB(PA_TACTIVATE), + new_peer_pa_tactivate); + } + +out: + return ret; +} + static void ufshcd_tune_unipro_params(struct ufs_hba *hba) { if (ufshcd_is_unipro_pa_params_tuning_req(hba)) { @@ -6494,6 +6564,9 @@ static void ufshcd_tune_unipro_params(struct ufs_hba *hba) if (hba->dev_quirks & UFS_DEVICE_QUIRK_PA_TACTIVATE) /* set 1ms timeout for PA_TACTIVATE */ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE), 10); + + if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE) + ufshcd_quirk_tune_host_pa_tactivate(hba); } static void ufshcd_clear_dbg_ufs_stats(struct ufs_hba *hba) diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h index bb95c2d5641a..602e196e9249 100644 --- a/drivers/scsi/ufs/unipro.h +++ b/drivers/scsi/ufs/unipro.h @@ -91,6 +91,7 @@ #define PA_MAXRXHSGEAR 0x1587 #define PA_RXHSUNTERMCAP 0x15A5 #define PA_RXLSTERMCAP 0x15A6 +#define PA_GRANULARITY 0x15AA #define PA_PACPREQTIMEOUT 0x1590 #define PA_PACPREQEOBTIMEOUT 0x1591 #define PA_HIBERN8TIME 0x15A7 @@ -121,6 +122,9 @@ #define PA_TACTIVATE_TIME_UNIT_US 10 #define PA_HIBERN8_TIME_UNIT_US 100 +#define PA_GRANULARITY_MIN_VAL 1 +#define PA_GRANULARITY_MAX_VAL 6 + /* PHY Adapter Protocol Constants */ #define PA_MAXDATALANES 4 |
