From: Palash Kambar quic_pkambar@quicinc.com
[ Upstream commit c1553fc105dff28f79bef90fab207235f5f2d977 ]
Currently, the UFS lane clocks remain enabled even after the link enters the Hibern8 state and are only disabled during runtime/system suspend.This patch modifies the behavior to disable the lane clocks during ufs_qcom_setup_clocks(), which is invoked shortly after the link enters Hibern8 via gate work.
While hibern8_notify() offers immediate control, toggling clocks on every transition isn't ideal due to varied contexts like clock scaling. Since setup_clocks() manages PHY/controller resources and is invoked soon after Hibern8 entry, it serves as a central and stable point for clock gating.
Signed-off-by: Palash Kambar quic_pkambar@quicinc.com Reviewed-by: Manivannan Sadhasivam mani@kernel.org Message-ID: 20250909055149.2068737-1-quic_pkambar@quicinc.com Signed-off-by: Martin K. Petersen martin.petersen@oracle.com Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
YES – the change fixes a real power-management bug with minimal, well- scoped risk.
- `drivers/ufs/host/ufs-qcom.c:1226` now shuts the lane clocks off when `ufshcd_setup_clocks(hba, false)` runs while the link is in Hibern8. Without this, the lane clocks stayed on after the gate work forced Hibern8 (see `drivers/ufs/core/ufshcd.c:2038-2054`), so clock gating never delivered the expected idle power savings—lane clocks previously only dropped during the much rarer runtime/system suspend path (`drivers/ufs/host/ufs-qcom.c:739`). - `drivers/ufs/host/ufs-qcom.c:1192-1200` symmetrically re-enable the lane clocks before the controller leaves Hibern8, so existing resume/ungate flows remain intact. The helper already handles errors in the same way other call sites (e.g., resume) do, so the added `dev_err(...)` path doesn’t introduce new behavior beyond propagating a genuine enabling failure. - The patch touches only the Qualcomm variant, relies on helpers already present in stable branches (older trees use the per-lane helpers but the hook points are identical), and doesn’t alter any interfaces or broader subsystem behavior. Backporting just requires adding the same on/off checks in the older `ufs_qcom_setup_clocks()` body.
Given that it restores the intended low-power behavior for idle gating on Qualcomm UFS hosts and stays tightly contained, it’s a good candidate for stable. Suggested follow-up after backport: exercise runtime PM or idle-gating tests to confirm the link enters/leaves Hibern8 cleanly.
drivers/ufs/host/ufs-qcom.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index 3ea6b08d2b526..2b6eb377eec07 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -1183,6 +1183,13 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, case PRE_CHANGE: if (on) { ufs_qcom_icc_update_bw(host); + if (ufs_qcom_is_link_hibern8(hba)) { + err = ufs_qcom_enable_lane_clks(host); + if (err) { + dev_err(hba->dev, "enable lane clks failed, ret=%d\n", err); + return err; + } + } } else { if (!ufs_qcom_is_link_active(hba)) { /* disable device ref_clk */ @@ -1208,6 +1215,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, if (ufshcd_is_hs_mode(&hba->pwr_info)) ufs_qcom_dev_ref_clk_ctrl(host, true); } else { + if (ufs_qcom_is_link_hibern8(hba)) + ufs_qcom_disable_lane_clks(host); + ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MIN][0][0].mem_bw, ufs_qcom_bw_table[MODE_MIN][0][0].cfg_bw); }