MediaTek UFS clocks are separated to two parts and controlled by different modules: ufs-mediatek and phy-ufs-mediatek. If both Auto-Hibern8 and clk-gating feature are enabled, mphy power control is not balanced thus unbalanced control also happens to the clocks probed by phy-ufs-mediatek module. Fix this issue by - Promise usage of phy_power_on/off balanced - Remove phy_power_on/off control in suspend/resume vops since both can be handled in setup_clock vops only Signed-off-by: Stanley Chu <stanley.chu@xxxxxxxxxxxx> Reviewed-by: Peter Wang <peter.wang@xxxxxxxxxxxx> --- drivers/scsi/ufs/ufs-mediatek.c | 56 +++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c index 5f41b7b7db8f..de9e643fb8dd 100644 --- a/drivers/scsi/ufs/ufs-mediatek.c +++ b/drivers/scsi/ufs/ufs-mediatek.c @@ -205,6 +205,23 @@ int ufs_mtk_wait_link_state(struct ufs_hba *hba, u32 state, return -ETIMEDOUT; } +static void ufs_mtk_mphy_power_on(struct ufs_hba *hba, bool on) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + struct phy *mphy = host->mphy; + + if (!mphy) + return; + + if (on && !host->mphy_powered_on) + phy_power_on(mphy); + else if (!on && host->mphy_powered_on) + phy_power_off(mphy); + else + return; + host->mphy_powered_on = on; +} + /** * ufs_mtk_setup_clocks - enables/disable clocks * @hba: host controller instance @@ -228,25 +245,24 @@ static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on, return 0; if (!on && status == PRE_CHANGE) { - if (!ufshcd_is_link_active(hba)) { - ufs_mtk_setup_ref_clk(hba, on); - ret = phy_power_off(host->mphy); - } else { - /* - * Gate ref-clk if link state is in Hibern8 - * triggered by Auto-Hibern8. - */ - if (!ufshcd_can_hibern8_during_gating(hba) && - ufshcd_is_auto_hibern8_enabled(hba)) { - ret = ufs_mtk_wait_link_state(hba, - VS_LINK_HIBERN8, - 15); - if (!ret) - ufs_mtk_setup_ref_clk(hba, on); + /* + * Gate ref-clk and poweroff mphy if link state is in OFF + * or Hibern8 by either ufshcd_link_state_transition() or + * Auto-Hibern8. + */ + if (!ufshcd_is_link_active(hba) || + (!ufshcd_can_hibern8_during_gating(hba) && + ufshcd_is_auto_hibern8_enabled(hba))) { + ret = ufs_mtk_wait_link_state(hba, + VS_LINK_HIBERN8, + 15); + if (!ret) { + ufs_mtk_setup_ref_clk(hba, on); + ufs_mtk_mphy_power_on(hba, on); } } } else if (on && status == POST_CHANGE) { - ret = phy_power_on(host->mphy); + ufs_mtk_mphy_power_on(hba, on); ufs_mtk_setup_ref_clk(hba, on); } @@ -538,7 +554,6 @@ static void ufs_mtk_vreg_set_lpm(struct ufs_hba *hba, bool lpm) static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) { int err; - struct ufs_mtk_host *host = ufshcd_get_variant(hba); if (ufshcd_is_link_hibern8(hba)) { err = ufs_mtk_link_set_lpm(hba); @@ -559,20 +574,13 @@ static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) ufs_mtk_vreg_set_lpm(hba, true); } - if (!ufshcd_is_link_active(hba)) - phy_power_off(host->mphy); - return 0; } static int ufs_mtk_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) { - struct ufs_mtk_host *host = ufshcd_get_variant(hba); int err; - if (!ufshcd_is_link_active(hba)) - phy_power_on(host->mphy); - if (ufshcd_is_link_hibern8(hba)) { ufs_mtk_vreg_set_lpm(hba, false); err = ufs_mtk_link_set_hpm(hba); -- 2.18.0