scsi: ufs: host: mediatek: Fix auto-hibern8 timer configuration

[ 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@mediatek.com>
Link: https://lore.kernel.org/r/20250811131423.3444014-3-peter.wang@mediatek.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Peter Wang
2025-08-11 21:11:18 +08:00
committed by Greg Kroah-Hartman
parent 68245d8fe2
commit 137dea7d7d

View File

@@ -847,6 +847,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);
@@ -1119,32 +1182,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,
@@ -1444,6 +1485,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,