[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20251025160905.3857885-388-sashal@kernel.org>
Date: Sat, 25 Oct 2025 12:00:19 -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: Fix auto-hibern8 timer configuration
From: Peter Wang <peter.wang@...iatek.com>
[ Upstream commit aa86602a483ba48f51044fbaefa1ebbf6da194a4 ]
Move the configuration of the Auto-Hibern8 (AHIT) timer from the
post-link stage to the 'fixup_dev_quirks' function. This change allows
setting the AHIT based on the vendor requirements:
(a) Samsung: 3.5 ms
(b) Micron: 2 ms
(c) Others: 1 ms
Additionally, the clock gating timer is adjusted based on the AHIT
scale, with a maximum setting of 10 ms. This ensures that the clock
gating delay is appropriately configured to match the AHIT settings.
Signed-off-by: Peter Wang <peter.wang@...iatek.com>
Link: https://lore.kernel.org/r/20250811131423.3444014-3-peter.wang@mediatek.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
- Fixes a real bug affecting users. Today the driver unconditionally
programs AH8 to 10 ms during link bring-up and derives the clock-
gating delay from only the AHIT timer field, ignoring the scale. That
yields incorrect behavior when a device needs a vendor-specific AH8
value or when the AHIT scale is not 1 ms. The patch:
- Removes the hardcoded AH8 value from `ufs_mtk_post_link()` in
`drivers/ufs/host/ufs-mediatek.c` and defers programming until
device info is known.
- Adds `ufs_mtk_fix_ahit()` to set `hba->ahit` based on the UFS
vendor: Samsung 3.5 ms, Micron 2 ms, others 1 ms.
- Introduces `ufs_mtk_us_to_ahit()` so the AHIT encoding matches the
HCI (same logic as the core sysfs helper).
- Reworks `ufs_mtk_setup_clk_gating()` to derive the delay from the
full AHIT value (timer + scale), avoiding the previous scale bug.
- Correct stage for AHIT programming. Moving the AHIT setup from link
POST_CHANGE to the device-quirk fixup stage is correct because the
vendor ID isn’t known at `POST_CHANGE`. The fix happens in
`ufs_mtk_fixup_dev_quirks()` which runs after reading device
descriptors (see core flow in `drivers/ufs/core/ufshcd.c:8380` calling
`ufs_fixup_device_setup(hba)`), and before the core writes AHIT to
hardware (`ufshcd_configure_auto_hibern8()` at
`drivers/ufs/core/ufshcd.c:8967`). Hence the right AHIT gets
programmed without extra transitions.
- Fixes a concrete correctness issue in clock-gating. Previously
`ufs_mtk_setup_clk_gating()` computed the delay as `ah_ms =
FIELD_GET(UFSHCI_AHIBERN8_TIMER_MASK, hba->ahit)` and then
`ufshcd_clkgate_delay_set(..., ah_ms + 5)`. That ignores the AHIT
scale and is only correct if the scale is 1 ms (which the driver
forcibly set earlier). The patch:
- Parses both AHIT scale and timer and converts to milliseconds via a
`scale_us[]` table before setting the gating delay. This fixes
gating delay when vendors require non-ms scales.
- Sets a minimum gating delay of 10 ms (`delay_ms = max(ah_ms, 10U)`)
to avoid overly aggressive gating when AHIT is small (1–3.5 ms).
This is a conservative, low-risk change that reduces churn.
- Small, contained change with minimal regression risk.
- Scope: one driver file (`drivers/ufs/host/ufs-mediatek.c`), no API
or architectural changes.
- Behavior: only affects Mediatek UFS host behavior and only when AH8
is supported and enabled.
- The vendor-based AHIT values are bounded and modest (1–3.5 ms), and
the gating floor of 10 ms is conservative.
- The patch respects `ufshcd_is_auto_hibern8_supported()` and won’t
alter systems where AH8 is disabled (driver already handles
disabling AH8; see `drivers/ufs/host/ufs-mediatek.c:258`).
- Alignment with core defaults and flow. The core sets a default AHIT
(150 ms) only if none is set earlier
(`drivers/ufs/core/ufshcd.c:10679`). The mediatek driver previously
overwrote this to 10 ms unconditionally at `POST_CHANGE`. The new
approach correctly overrides the default with vendor-specific AHIT at
quirk-fixup time and before the core writes the register, making the
effective setting both correct and deterministic.
- Backport notes and considerations.
- The quirk-fixup hook must be present in the target stable branch
(`ufshcd_vops_fixup_dev_quirks()` and call site exist in current
stable series; see `drivers/ufs/core/ufshcd-priv.h:195` and
`drivers/ufs/core/ufshcd.c:8380`).
- The helper macros and fields used (e.g., `UFSHCI_AHIBERN8_*`,
`UFS_VENDOR_*`, `hba->clk_gating.delay_ms`) are present in
maintained stable branches.
- Minor nits: the patch updates `hba->clk_gating.delay_ms` under
`host->host_lock` instead of using `ufshcd_clkgate_delay_set()`,
which in core protects the assignment with `clk_gating.lock`.
Functionally it’s fine for a single-word store, but for consistency
you may prefer `ufshcd_clkgate_delay_set(hba->dev, max(ah_ms, 10U))`
when backporting to preserve locking semantics.
- The commit message says “maximum setting of 10 ms,” but the code
enforces a minimum of 10 ms via `max(ah_ms, 10U)`. The
implementation is the safer choice and aligns with the intent to
avoid too-aggressive gating.
Conclusion: This is a targeted bug fix that corrects AHIT configuration
timing, applies vendor requirements, and fixes the gating-delay
calculation to account for AHIT scale. It’s small, self-contained, and
low risk. It is suitable for backporting to stable kernel trees.
drivers/ufs/host/ufs-mediatek.c | 86 ++++++++++++++++++++++++---------
1 file changed, 64 insertions(+), 22 deletions(-)
diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c
index f902ce08c95a6..8dd124835151a 100644
--- a/drivers/ufs/host/ufs-mediatek.c
+++ b/drivers/ufs/host/ufs-mediatek.c
@@ -1075,6 +1075,69 @@ static void ufs_mtk_vreg_fix_vccqx(struct ufs_hba *hba)
}
}
+static void ufs_mtk_setup_clk_gating(struct ufs_hba *hba)
+{
+ unsigned long flags;
+ u32 ah_ms = 10;
+ u32 ah_scale, ah_timer;
+ u32 scale_us[] = {1, 10, 100, 1000, 10000, 100000};
+
+ if (ufshcd_is_clkgating_allowed(hba)) {
+ if (ufshcd_is_auto_hibern8_supported(hba) && hba->ahit) {
+ ah_scale = FIELD_GET(UFSHCI_AHIBERN8_SCALE_MASK,
+ hba->ahit);
+ ah_timer = FIELD_GET(UFSHCI_AHIBERN8_TIMER_MASK,
+ hba->ahit);
+ if (ah_scale <= 5)
+ ah_ms = ah_timer * scale_us[ah_scale] / 1000;
+ }
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ hba->clk_gating.delay_ms = max(ah_ms, 10U);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ }
+}
+
+/* Convert microseconds to Auto-Hibernate Idle Timer register value */
+static u32 ufs_mtk_us_to_ahit(unsigned int timer)
+{
+ unsigned int scale;
+
+ for (scale = 0; timer > UFSHCI_AHIBERN8_TIMER_MASK; ++scale)
+ timer /= UFSHCI_AHIBERN8_SCALE_FACTOR;
+
+ return FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, timer) |
+ FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, scale);
+}
+
+static void ufs_mtk_fix_ahit(struct ufs_hba *hba)
+{
+ unsigned int us;
+
+ if (ufshcd_is_auto_hibern8_supported(hba)) {
+ switch (hba->dev_info.wmanufacturerid) {
+ case UFS_VENDOR_SAMSUNG:
+ /* configure auto-hibern8 timer to 3.5 ms */
+ us = 3500;
+ break;
+
+ case UFS_VENDOR_MICRON:
+ /* configure auto-hibern8 timer to 2 ms */
+ us = 2000;
+ break;
+
+ default:
+ /* configure auto-hibern8 timer to 1 ms */
+ us = 1000;
+ break;
+ }
+
+ hba->ahit = ufs_mtk_us_to_ahit(us);
+ }
+
+ ufs_mtk_setup_clk_gating(hba);
+}
+
static void ufs_mtk_init_mcq_irq(struct ufs_hba *hba)
{
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
@@ -1369,32 +1432,10 @@ static int ufs_mtk_pre_link(struct ufs_hba *hba)
return ret;
}
-
-static void ufs_mtk_setup_clk_gating(struct ufs_hba *hba)
-{
- u32 ah_ms;
-
- if (ufshcd_is_clkgating_allowed(hba)) {
- if (ufshcd_is_auto_hibern8_supported(hba) && hba->ahit)
- ah_ms = FIELD_GET(UFSHCI_AHIBERN8_TIMER_MASK,
- hba->ahit);
- else
- ah_ms = 10;
- ufshcd_clkgate_delay_set(hba->dev, ah_ms + 5);
- }
-}
-
static void ufs_mtk_post_link(struct ufs_hba *hba)
{
/* enable unipro clock gating feature */
ufs_mtk_cfg_unipro_cg(hba, true);
-
- /* will be configured during probe hba */
- if (ufshcd_is_auto_hibern8_supported(hba))
- hba->ahit = FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 10) |
- FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3);
-
- ufs_mtk_setup_clk_gating(hba);
}
static int ufs_mtk_link_startup_notify(struct ufs_hba *hba,
@@ -1726,6 +1767,7 @@ static void ufs_mtk_fixup_dev_quirks(struct ufs_hba *hba)
ufs_mtk_vreg_fix_vcc(hba);
ufs_mtk_vreg_fix_vccqx(hba);
+ ufs_mtk_fix_ahit(hba);
}
static void ufs_mtk_event_notify(struct ufs_hba *hba,
--
2.51.0
Powered by blists - more mailing lists