scsi: ufs: mediatek: Support vops pre suspend to disable auto-hibern8
authorPeter Wang <peter.wang@mediatek.com>
Wed, 6 Oct 2021 05:47:05 +0000 (13:47 +0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 12 Oct 2021 17:41:01 +0000 (13:41 -0400)
Mediatek UFS needs auto-hibern8 disabled before suspend. Introduce a
solution to do pre-suspend before SSU (sleep).

Link: https://lore.kernel.org/r/20211006054705.21885-1-peter.wang@mediatek.com
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Bean Huo <beanhuo@micron.com>
Signed-off-by: Peter Wang <peter.wang@mediatek.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/ufs/ufs-exynos.c
drivers/scsi/ufs/ufs-hisi.c
drivers/scsi/ufs/ufs-mediatek.c
drivers/scsi/ufs/ufs-mediatek.h
drivers/scsi/ufs/ufs-qcom.c
drivers/scsi/ufs/ufshcd.c
drivers/scsi/ufs/ufshcd.h

index a14dd8c..b2ec9e2 100644 (file)
@@ -1176,10 +1176,14 @@ static void exynos_ufs_hibern8_notify(struct ufs_hba *hba,
        }
 }
 
-static int exynos_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+static int exynos_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
+       enum ufs_notify_change_status status)
 {
        struct exynos_ufs *ufs = ufshcd_get_variant(hba);
 
+       if (status == PRE_CHANGE)
+               return 0;
+
        if (!ufshcd_is_link_active(hba))
                phy_power_off(ufs->phy);
 
index 6b706de..8c7e8d3 100644 (file)
@@ -396,10 +396,14 @@ out:
        return ret;
 }
 
-static int ufs_hisi_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+static int ufs_hisi_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
+       enum ufs_notify_change_status status)
 {
        struct ufs_hisi_host *host = ufshcd_get_variant(hba);
 
+       if (status == PRE_CHANGE)
+               return 0;
+
        if (pm_op == UFS_RUNTIME_PM)
                return 0;
 
index d2d7e76..d1696db 100644 (file)
@@ -311,6 +311,46 @@ static void ufs_mtk_dbg_sel(struct ufs_hba *hba)
        }
 }
 
+static void ufs_mtk_wait_idle_state(struct ufs_hba *hba,
+                           unsigned long retry_ms)
+{
+       u64 timeout, time_checked;
+       u32 val, sm;
+       bool wait_idle;
+
+       timeout = sched_clock() + retry_ms * 1000000UL;
+
+
+       /* wait a specific time after check base */
+       udelay(10);
+       wait_idle = false;
+
+       do {
+               time_checked = sched_clock();
+               ufs_mtk_dbg_sel(hba);
+               val = ufshcd_readl(hba, REG_UFS_PROBE);
+
+               sm = val & 0x1f;
+
+               /*
+                * if state is in H8 enter and H8 enter confirm
+                * wait until return to idle state.
+                */
+               if ((sm >= VS_HIB_ENTER) && (sm <= VS_HIB_EXIT)) {
+                       wait_idle = true;
+                       udelay(50);
+                       continue;
+               } else if (!wait_idle)
+                       break;
+
+               if (wait_idle && (sm == VS_HCE_BASE))
+                       break;
+       } while (time_checked < timeout);
+
+       if (wait_idle && sm != VS_HCE_BASE)
+               dev_info(hba->dev, "wait idle tmo: 0x%x\n", val);
+}
+
 static int ufs_mtk_wait_link_state(struct ufs_hba *hba, u32 state,
                                   unsigned long max_wait_ms)
 {
@@ -949,11 +989,37 @@ static void ufs_mtk_vreg_set_lpm(struct ufs_hba *hba, bool lpm)
                                   REGULATOR_MODE_NORMAL);
 }
 
-static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+static void ufs_mtk_auto_hibern8_disable(struct ufs_hba *hba)
+{
+       unsigned long flags;
+       int ret;
+
+       /* disable auto-hibern8 */
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       ufshcd_writel(hba, 0, REG_AUTO_HIBERNATE_IDLE_TIMER);
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+       /* wait host return to idle state when auto-hibern8 off */
+       ufs_mtk_wait_idle_state(hba, 5);
+
+       ret = ufs_mtk_wait_link_state(hba, VS_LINK_UP, 100);
+       if (ret)
+               dev_warn(hba->dev, "exit h8 state fail, ret=%d\n", ret);
+}
+
+static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
+       enum ufs_notify_change_status status)
 {
        int err;
        struct arm_smccc_res res;
 
+       if (status == PRE_CHANGE) {
+               if (!ufshcd_is_auto_hibern8_supported(hba))
+                       return 0;
+               ufs_mtk_auto_hibern8_disable(hba);
+               return 0;
+       }
+
        if (ufshcd_is_link_hibern8(hba)) {
                err = ufs_mtk_link_set_lpm(hba);
                if (err)
index 524c8e2..c96b9b5 100644 (file)
@@ -55,6 +55,26 @@ enum {
 };
 
 /*
+ * Vendor specific host controller state
+ */
+enum {
+       VS_HCE_RESET                = 0,
+       VS_HCE_BASE                 = 1,
+       VS_HCE_OOCPR_WAIT           = 2,
+       VS_HCE_DME_RESET            = 3,
+       VS_HCE_MIDDLE               = 4,
+       VS_HCE_DME_ENABLE           = 5,
+       VS_HCE_DEFAULTS             = 6,
+       VS_HIB_IDLEEN               = 7,
+       VS_HIB_ENTER                = 8,
+       VS_HIB_ENTER_CONF           = 9,
+       VS_HIB_MIDDLE               = 10,
+       VS_HIB_WAITTIMER            = 11,
+       VS_HIB_EXIT_CONF            = 12,
+       VS_HIB_EXIT                 = 13,
+};
+
+/*
  * SiP commands
  */
 #define MTK_SIP_UFS_CONTROL               MTK_SIP_SMC_CMD(0x276)
index 92f5bb4..0d2e950 100644 (file)
@@ -589,11 +589,15 @@ static void ufs_qcom_device_reset_ctrl(struct ufs_hba *hba, bool asserted)
        gpiod_set_value_cansleep(host->device_reset, asserted);
 }
 
-static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
+       enum ufs_notify_change_status status)
 {
        struct ufs_qcom_host *host = ufshcd_get_variant(hba);
        struct phy *phy = host->generic_phy;
 
+       if (status == PRE_CHANGE)
+               return 0;
+
        if (ufs_qcom_is_link_off(hba)) {
                /*
                 * Disable the tx/rx lane symbol clocks before PHY is
index 9283ab4..3de380a 100644 (file)
@@ -8844,6 +8844,10 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
 
        flush_work(&hba->eeh_work);
 
+       ret = ufshcd_vops_suspend(hba, pm_op, PRE_CHANGE);
+       if (ret)
+               goto enable_scaling;
+
        if (req_dev_pwr_mode != hba->curr_dev_pwr_mode) {
                if (pm_op != UFS_RUNTIME_PM)
                        /* ensure that bkops is disabled */
@@ -8871,7 +8875,7 @@ vops_suspend:
         * vendor specific host controller register space call them before the
         * host clocks are ON.
         */
-       ret = ufshcd_vops_suspend(hba, pm_op);
+       ret = ufshcd_vops_suspend(hba, pm_op, POST_CHANGE);
        if (ret)
                goto set_link_active;
        goto out;
@@ -8999,7 +9003,8 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
 set_old_link_state:
        ufshcd_link_state_transition(hba, old_link_state, 0);
 vendor_suspend:
-       ufshcd_vops_suspend(hba, pm_op);
+       ufshcd_vops_suspend(hba, pm_op, PRE_CHANGE);
+       ufshcd_vops_suspend(hba, pm_op, POST_CHANGE);
 out:
        if (ret)
                ufshcd_update_evt_hist(hba, UFS_EVT_WL_RES_ERR, (u32)ret);
index a50dfda..64ce723 100644 (file)
@@ -344,7 +344,8 @@ struct ufs_hba_variant_ops {
                                        enum ufs_notify_change_status);
        int     (*apply_dev_quirks)(struct ufs_hba *hba);
        void    (*fixup_dev_quirks)(struct ufs_hba *hba);
-       int     (*suspend)(struct ufs_hba *, enum ufs_pm_op);
+       int     (*suspend)(struct ufs_hba *, enum ufs_pm_op,
+                                       enum ufs_notify_change_status);
        int     (*resume)(struct ufs_hba *, enum ufs_pm_op);
        void    (*dbg_register_dump)(struct ufs_hba *hba);
        int     (*phy_initialization)(struct ufs_hba *);
@@ -1318,10 +1319,11 @@ static inline void ufshcd_vops_fixup_dev_quirks(struct ufs_hba *hba)
                hba->vops->fixup_dev_quirks(hba);
 }
 
-static inline int ufshcd_vops_suspend(struct ufs_hba *hba, enum ufs_pm_op op)
+static inline int ufshcd_vops_suspend(struct ufs_hba *hba, enum ufs_pm_op op,
+                               enum ufs_notify_change_status status)
 {
        if (hba->vops && hba->vops->suspend)
-               return hba->vops->suspend(hba, op);
+               return hba->vops->suspend(hba, op, status);
 
        return 0;
 }