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:
Peter Wang 2021-10-06 13:47:05 +08:00 коммит произвёл Martin K. Petersen
Родитель f4875d509a
Коммит 9561f58442
7 изменённых файлов: 114 добавлений и 9 удалений

Просмотреть файл

@ -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;
}