scsi: ufs: ensure that host pa_tactivate is higher than device
authorsubhashj@codeaurora.org <subhashj@codeaurora.org>
Thu, 24 Nov 2016 00:32:08 +0000 (16:32 -0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 29 Nov 2016 17:06:56 +0000 (12:06 -0500)
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.

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>
drivers/scsi/ufs/ufs_quirks.h
drivers/scsi/ufs/ufshcd.c
drivers/scsi/ufs/unipro.h

index 22f881e9253a256a1614df77048081cafc12efdb..f7983058f3f716ed27505fbcef0d420eaf8a6163 100644 (file)
@@ -128,6 +128,13 @@ struct ufs_dev_fix {
  */
 #define UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM      (1 << 6)
 
+/*
+ * 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 << 7)
+
+
 struct ufs_hba;
 void ufs_advertise_fixup_device(struct ufs_hba *hba);
 
@@ -140,6 +147,8 @@ static struct ufs_dev_fix ufs_fixups[] = {
                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",
index 393f6d55df5af3d45379a248d6afea95b0a136c7..28cbca21c4dd49437e7824c75897ead0d568bbdd 100644 (file)
@@ -5089,6 +5089,76 @@ out:
        return ret;
 }
 
+/**
+ * ufshcd_quirk_tune_host_pa_tactivate - Ensures that host PA_TACTIVATE is
+ * less 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)) {
@@ -5099,6 +5169,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);
 }
 
 /**
index eff8b567557532090f745779ce92f89d2171b94b..23129d7b2678dfea93b21f010c31a7aece9e9233 100644 (file)
 #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
 #define VS_DEBUGOMC            0xD09E
 #define VS_POWERSTATE          0xD083
 
+#define PA_GRANULARITY_MIN_VAL 1
+#define PA_GRANULARITY_MAX_VAL 6
+
 /* PHY Adapter Protocol Constants */
 #define PA_MAXDATALANES        4