[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20251025160905.3857885-150-sashal@kernel.org>
Date: Sat, 25 Oct 2025 11:56:21 -0400
From: Sasha Levin <sashal@...nel.org>
To: patches@...ts.linux.dev,
stable@...r.kernel.org
Cc: Peter Wang <peter.wang@...iatek.com>,
"Martin K. Petersen" <martin.petersen@...cle.com>,
Sasha Levin <sashal@...nel.org>,
matthias.bgg@...il.com,
angelogioacchino.delregno@...labora.com,
linux-scsi@...r.kernel.org,
linux-mediatek@...ts.infradead.org,
linux-kernel@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org
Subject: [PATCH AUTOSEL 6.17-6.6] scsi: ufs: host: mediatek: Disable auto-hibern8 during power mode changes
From: Peter Wang <peter.wang@...iatek.com>
[ Upstream commit f5ca8d0c7a6388abd5d8023cc682e1543728cc73 ]
Disable auto-hibern8 during power mode transitions to prevent unintended
entry into auto-hibern8. Restore the original auto-hibern8 timer value
after completing the power mode change to maintain system stability and
prevent potential issues during power state transitions.
Signed-off-by: Peter Wang <peter.wang@...iatek.com>
Signed-off-by: Martin K. Petersen <martin.petersen@...cle.com>
Signed-off-by: Sasha Levin <sashal@...nel.org>
---
LLM Generated explanations, may be completely bogus:
YES
Summary
- The change disables Auto-Hibern8 (AH8) around UFS power mode
transitions and restores the prior timer afterward. This prevents
unintended AH8 entry while the link is being reconfigured, which can
cause timeouts or recovery events during transitions. The fix is
small, self-contained, and limited to the Mediatek UFS host driver.
What the patch does
- Saves current AH8 timer and disables AH8 in PRE_CHANGE:
- drivers/ufs/host/ufs-mediatek.c:1472–1476
- Reads `REG_AUTO_HIBERNATE_IDLE_TIMER` into a static `reg` and
calls `ufs_mtk_auto_hibern8_disable(hba)`.
- Disables AH8 in a helper and ensures the link is up before proceeding:
- drivers/ufs/host/ufs-mediatek.c:1436–1461
- Writes 0 to `REG_AUTO_HIBERNATE_IDLE_TIMER` (disables AH8), waits
for the host idle state, then waits for `VS_LINK_UP`. On failure,
warns and triggers `ufshcd_force_error_recovery(hba)` and returns
`-EBUSY`.
- Restores the previous AH8 timer in POST_CHANGE:
- drivers/ufs/host/ufs-mediatek.c:1480–1483
Why this fixes a bug
- Power mode transitions involve DME configuration and link parameter
changes (see setup/adaptation in `ufs_mtk_pre_pwr_change()`:
drivers/ufs/host/ufs-mediatek.c:1405–1434). If the link enters AH8
mid-transition, the controller and device can deadlock or time out,
requiring error recovery. Temporarily disabling AH8 ensures the link
stays in the expected state while power mode changes occur and
restores normal power-saving afterwards.
- The helper already used in suspend PRE_CHANGE (drivers/ufs/host/ufs-
mediatek.c:1748–1751) shows the driver’s established pattern to
disable AH8 before low-power transitions; extending this to power mode
changes closes a similar race.
Scope and risk
- Scope: One driver file; no UFS core changes; no architectural shifts.
Uses existing helpers (`ufshcd_is_auto_hibern8_supported`,
`ufshcd_readl/writel`, `ufs_mtk_wait_*`,
`ufshcd_force_error_recovery`).
- Regression risk: Low. Behavior change is to temporarily disable AH8
only during power mode changes and then restore the previous timer.
- Note: `ufs_mtk_pwr_change_notify()` stores the old AH8 timer in a
function-scope static (`reg`) (drivers/ufs/host/ufs-
mediatek.c:1469). While typical Mediatek systems have a single UFS
host and power mode changes are serialized, a static variable is
theoretically shared if multiple controllers triggered this
concurrently. In practice, impact is negligible on common
configurations.
- Note: The PRE_CHANGE path does not propagate the return of
`ufs_mtk_auto_hibern8_disable()` (drivers/ufs/host/ufs-
mediatek.c:1473–1478). The helper triggers error recovery internally
and returns `-EBUSY`, so recovery still occurs, but the immediate
PRE_CHANGE return value won’t reflect the failure. A tiny follow-up
improvement would propagate this error directly (as done later in-
tree).
Evidence of established pattern
- Other vendor drivers also manage AH8 around sensitive transitions,
supporting this approach:
- Hisilicon disables/adjusts AH8 during link setup:
drivers/ufs/host/ufs-hisi.c:234–237
- Spreadtrum disables AH8 during suspend PRE_CHANGE:
drivers/ufs/host/ufs-sprd.c:185–190
Stable backport criteria
- Fixes a real operational bug (unintended AH8 mid-transition),
observable as timeouts or recovery during power mode changes.
- Small, targeted change in a single vendor driver; minimal risk to
other subsystems.
- No new features or architectural changes; follows established patterns
in UFS vendor drivers.
- While the commit message lacks an explicit Fixes/Stable tag, the
change aligns well with stable policy as a platform-specific
reliability fix.
Recommendation
- Backport to stable: YES.
- Optional but advisable: include the small follow-up that returns an
error immediately on idle wait timeout (to propagate the PRE_CHANGE
failure) to match the improved error handling now seen in-tree.
drivers/ufs/host/ufs-mediatek.c | 53 +++++++++++++++++++--------------
1 file changed, 30 insertions(+), 23 deletions(-)
diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c
index 91081d2aabe44..3defb5f135e33 100644
--- a/drivers/ufs/host/ufs-mediatek.c
+++ b/drivers/ufs/host/ufs-mediatek.c
@@ -1400,19 +1400,49 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba,
return ret;
}
+static int ufs_mtk_auto_hibern8_disable(struct ufs_hba *hba)
+{
+ int ret;
+
+ /* disable auto-hibern8 */
+ ufshcd_writel(hba, 0, REG_AUTO_HIBERNATE_IDLE_TIMER);
+
+ /* 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);
+
+ ufshcd_force_error_recovery(hba);
+
+ /* trigger error handler and break suspend */
+ ret = -EBUSY;
+ }
+
+ return ret;
+}
+
static int ufs_mtk_pwr_change_notify(struct ufs_hba *hba,
enum ufs_notify_change_status stage,
const struct ufs_pa_layer_attr *dev_max_params,
struct ufs_pa_layer_attr *dev_req_params)
{
int ret = 0;
+ static u32 reg;
switch (stage) {
case PRE_CHANGE:
+ if (ufshcd_is_auto_hibern8_supported(hba)) {
+ reg = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER);
+ ufs_mtk_auto_hibern8_disable(hba);
+ }
ret = ufs_mtk_pre_pwr_change(hba, dev_max_params,
dev_req_params);
break;
case POST_CHANGE:
+ if (ufshcd_is_auto_hibern8_supported(hba))
+ ufshcd_writel(hba, reg, REG_AUTO_HIBERNATE_IDLE_TIMER);
break;
default:
ret = -EINVAL;
@@ -1646,29 +1676,6 @@ static void ufs_mtk_dev_vreg_set_lpm(struct ufs_hba *hba, bool lpm)
}
}
-static int ufs_mtk_auto_hibern8_disable(struct ufs_hba *hba)
-{
- int ret;
-
- /* disable auto-hibern8 */
- ufshcd_writel(hba, 0, REG_AUTO_HIBERNATE_IDLE_TIMER);
-
- /* 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);
-
- ufshcd_force_error_recovery(hba);
-
- /* trigger error handler and break suspend */
- ret = -EBUSY;
- }
-
- return ret;
-}
-
static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
enum ufs_notify_change_status status)
{
--
2.51.0
Powered by blists - more mailing lists