scsi: ufs: mediatek: Support vops pre suspend to disable auto-hibern8
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>
This commit is contained in:
Родитель
f4875d509a
Коммит
9561f58442
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -54,6 +54,26 @@ enum {
|
|||
VS_LINK_CFG = 5,
|
||||
};
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче