From 6c00c493a344b65fb0a356cb3f328a647085687b Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Mon, 11 Aug 2025 21:11:17 +0800 Subject: [PATCH 01/10] scsi: ufs: host: mediatek: Simplify variable usage Simplify the code by using 'info->vcc' instead of 'hba->vreg_info.vcc', as they refer to the same value. This change improves code readability. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250811131423.3444014-2-peter.wang@mediatek.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 86ae73b89d4d..1d2df63457bf 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -1018,7 +1018,7 @@ static int ufs_mtk_vreg_fix_vcc(struct ufs_hba *hba) struct arm_smccc_res res; int err, ver; - if (hba->vreg_info.vcc) + if (info->vcc) return 0; if (of_property_read_bool(np, "mediatek,ufs-vcc-by-num")) { From aa86602a483ba48f51044fbaefa1ebbf6da194a4 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Mon, 11 Aug 2025 21:11:18 +0800 Subject: [PATCH 02/10] scsi: ufs: host: mediatek: Fix auto-hibern8 timer configuration 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 Link: https://lore.kernel.org/r/20250811131423.3444014-3-peter.wang@mediatek.com Signed-off-by: Martin K. Petersen --- 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 1d2df63457bf..e1eac1b428f8 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, From f91c6c70d103a619c58aa02bddf3c27c6433556a Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Mon, 11 Aug 2025 21:11:19 +0800 Subject: [PATCH 03/10] scsi: ufs: host: mediatek: Add debug information for Auto-Hibern8 Enhance the clock gating logic by adding debug information for the Auto-Hibern8 (AHIT) register. This additional logging aids in troubleshooting by providing insights into the AHIT configuration when the clock is not turned off as expected. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250811131423.3444014-4-peter.wang@mediatek.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index e1eac1b428f8..32f8d8da381e 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -798,8 +798,14 @@ static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on, clk_pwr_off = true; } - if (clk_pwr_off) + if (clk_pwr_off) { ufs_mtk_pwr_ctrl(hba, false); + } else { + dev_warn(hba->dev, "Clock is not turned off, hba->ahit = 0x%x, AHIT = 0x%x\n", + hba->ahit, + ufshcd_readl(hba, + REG_AUTO_HIBERNATE_IDLE_TIMER)); + } ufs_mtk_mcq_disable_irq(hba); } else if (on && status == POST_CHANGE) { ufs_mtk_pwr_ctrl(hba, true); From 86a678a25108705657e2f3bb00c42f9e5e5d692b Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Mon, 11 Aug 2025 21:11:20 +0800 Subject: [PATCH 04/10] scsi: ufs: host: mediatek: Fine-tune clock scaling Disable clock scaling for UFS versions below 4.0. Clock scaling is unnecessary for these versions, and this change ensures that the feature is only enabled for compatible UFS versions. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250811131423.3444014-5-peter.wang@mediatek.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 32f8d8da381e..2b55e77d5100 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -29,6 +29,7 @@ #include "ufs-mediatek-sip.h" static int ufs_mtk_config_mcq(struct ufs_hba *hba, bool irq); +static void _ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up); #define CREATE_TRACE_POINTS #include "ufs-mediatek-trace.h" @@ -1144,6 +1145,17 @@ static void ufs_mtk_fix_ahit(struct ufs_hba *hba) ufs_mtk_setup_clk_gating(hba); } +static void ufs_mtk_fix_clock_scaling(struct ufs_hba *hba) +{ + /* UFS version is below 4.0, clock scaling is not necessary */ + if ((hba->dev_info.wspecversion < 0x0400) && + ufs_mtk_is_clk_scale_ready(hba)) { + hba->caps &= ~UFSHCD_CAP_CLK_SCALING; + + _ufs_mtk_clk_scale(hba, false); + } +} + static void ufs_mtk_init_mcq_irq(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); @@ -1774,6 +1786,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); + ufs_mtk_fix_clock_scaling(hba); } static void ufs_mtk_event_notify(struct ufs_hba *hba, From 7212d624f8638f8ea8ad1ecbb80622c7987bc7a1 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Mon, 11 Aug 2025 21:11:21 +0800 Subject: [PATCH 05/10] scsi: ufs: host: mediatek: Fix PWM mode switch issue Address a failure in switching to PWM mode by ensuring proper configuration of power modes and adaptation settings. The changes include checks for SLOW_MODE and adjustments to the desired working mode and adaptation configuration based on the device's power mode and hardware version. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250811131423.3444014-6-peter.wang@mediatek.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 2b55e77d5100..56f6dd01c470 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -1321,6 +1321,10 @@ static bool ufs_mtk_pmc_via_fastauto(struct ufs_hba *hba, dev_req_params->gear_rx < UFS_HS_G4) return false; + if (dev_req_params->pwr_tx == SLOW_MODE || + dev_req_params->pwr_rx == SLOW_MODE) + return false; + return true; } @@ -1336,6 +1340,10 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba, host_params.hs_rx_gear = UFS_HS_G5; host_params.hs_tx_gear = UFS_HS_G5; + if (dev_max_params->pwr_rx == SLOW_MODE || + dev_max_params->pwr_tx == SLOW_MODE) + host_params.desired_working_mode = UFS_PWM_MODE; + ret = ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params); if (ret) { pr_info("%s: failed to determine capabilities\n", @@ -1368,10 +1376,21 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba, } } - if (host->hw_ver.major >= 3) { + if (dev_req_params->pwr_rx == FAST_MODE || + dev_req_params->pwr_rx == FASTAUTO_MODE) { + if (host->hw_ver.major >= 3) { + ret = ufshcd_dme_configure_adapt(hba, + dev_req_params->gear_tx, + PA_INITIAL_ADAPT); + } else { + ret = ufshcd_dme_configure_adapt(hba, + dev_req_params->gear_tx, + PA_NO_ADAPT); + } + } else { ret = ufshcd_dme_configure_adapt(hba, - dev_req_params->gear_tx, - PA_INITIAL_ADAPT); + dev_req_params->gear_tx, + PA_NO_ADAPT); } return ret; From 979feee0cf43b32d288931649d7c6d9a5524ea55 Mon Sep 17 00:00:00 2001 From: Alice Chao Date: Mon, 11 Aug 2025 21:11:22 +0800 Subject: [PATCH 06/10] scsi: ufs: host: mediatek: Assign power mode userdata before FASTAUTO mode change Assign power mode userdata settings before transitioning to FASTAUTO power mode. This ensures that default timeout values are set for various parameters, enhancing the reliability and performance of the power mode change process. Signed-off-by: Alice Chao Reviewed-by: Peter Wang Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250811131423.3444014-7-peter.wang@mediatek.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 56f6dd01c470..bd6616598c33 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -1367,6 +1367,28 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba, ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXHSADAPTTYPE), PA_NO_ADAPT); + if (!(hba->quirks & UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING)) { + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0), + DL_FC0ProtectionTimeOutVal_Default); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1), + DL_TC0ReplayTimeOutVal_Default); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2), + DL_AFC0ReqTimeOutVal_Default); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA3), + DL_FC1ProtectionTimeOutVal_Default); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA4), + DL_TC1ReplayTimeOutVal_Default); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA5), + DL_AFC1ReqTimeOutVal_Default); + + ufshcd_dme_set(hba, UIC_ARG_MIB(DME_LocalFC0ProtectionTimeOutVal), + DL_FC0ProtectionTimeOutVal_Default); + ufshcd_dme_set(hba, UIC_ARG_MIB(DME_LocalTC0ReplayTimeOutVal), + DL_TC0ReplayTimeOutVal_Default); + ufshcd_dme_set(hba, UIC_ARG_MIB(DME_LocalAFC0ReqTimeOutVal), + DL_AFC0ReqTimeOutVal_Default); + } + ret = ufshcd_uic_change_pwr_mode(hba, FASTAUTO_MODE << 4 | FASTAUTO_MODE); From f1617ecf18aaf222482f683f44ab4b155976539d Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Mon, 11 Aug 2025 21:11:23 +0800 Subject: [PATCH 07/10] scsi: ufs: host: mediatek: Optimize power mode change handling Optimize the power mode change process by skipping the adaptation setting toggle if the requested power mode configuration is already applied. This enhancement reduces unnecessary operations, improving efficiency during power mode transitions. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250811131423.3444014-8-peter.wang@mediatek.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index bd6616598c33..1fb8a094d2af 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -1398,6 +1398,17 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba, } } + /* if already configured to the requested pwr_mode, skip adapt */ + if (dev_req_params->gear_rx == hba->pwr_info.gear_rx && + dev_req_params->gear_tx == hba->pwr_info.gear_tx && + dev_req_params->lane_rx == hba->pwr_info.lane_rx && + dev_req_params->lane_tx == hba->pwr_info.lane_tx && + dev_req_params->pwr_rx == hba->pwr_info.pwr_rx && + dev_req_params->pwr_tx == hba->pwr_info.pwr_tx && + dev_req_params->hs_rate == hba->pwr_info.hs_rate) { + return ret; + } + if (dev_req_params->pwr_rx == FAST_MODE || dev_req_params->pwr_rx == FASTAUTO_MODE) { if (host->hw_ver.major >= 3) { From bacb96ce06c05804fae4bfd38c4058b6d4c423b3 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Mon, 11 Aug 2025 21:11:24 +0800 Subject: [PATCH 08/10] scsi: ufs: host: mediatek: Fix UniPro setting for MT6989 Set the UniPro attribute 0xD09E[4] bit to enable the 1144 functions specifically for the MT6989 platform. This adjustment ensures proper functionality and compatibility with the MT6989 hardware. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250811131423.3444014-9-peter.wang@mediatek.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 1fb8a094d2af..e3d99981a802 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -1475,6 +1475,7 @@ static int ufs_mtk_pre_link(struct ufs_hba *hba) { int ret; u32 tmp; + struct ufs_mtk_host *host = ufshcd_get_variant(hba); ufs_mtk_get_controller_version(hba); @@ -1500,6 +1501,16 @@ static int ufs_mtk_pre_link(struct ufs_hba *hba) ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp); + /* Enable the 1144 functions setting */ + if (host->ip_ver == IP_VER_MT6989) { + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(VS_DEBUGOMC), &tmp); + if (ret) + return ret; + + tmp |= 0x10; + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_DEBUGOMC), tmp); + } + return ret; } static void ufs_mtk_post_link(struct ufs_hba *hba) From 878ed88c50bfb14d972dd3b86a1c8188c58de4e5 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Mon, 11 Aug 2025 21:11:25 +0800 Subject: [PATCH 09/10] scsi: ufs: host: mediatek: Change reset sequence for improved stability Modify the reset sequence to ensure that the device reset pin is set low before the host is disabled. This change enhances the stability of the reset process by ensuring the correct order of operations. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250811131423.3444014-10-peter.wang@mediatek.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index e3d99981a802..647b6eb05398 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -1543,11 +1543,11 @@ static int ufs_mtk_device_reset(struct ufs_hba *hba) { struct arm_smccc_res res; - /* disable hba before device reset */ - ufshcd_hba_stop(hba); - ufs_mtk_device_reset_ctrl(0, res); + /* disable hba in middle of device reset */ + ufshcd_hba_stop(hba); + /* * The reset signal is active low. UFS devices shall detect * more than or equal to 1us of positive or negative RST_n From 5863638598f5e4f64d2f85b03f376383ca1f2ab7 Mon Sep 17 00:00:00 2001 From: Alice Chao Date: Mon, 11 Aug 2025 21:11:26 +0800 Subject: [PATCH 10/10] scsi: ufs: host: mediatek: Fix invalid access in vccqx handling Add a NULL check before accessing the 'vccqx' pointer to prevent invalid memory access. This ensures that the function safely handles cases where 'vccq' and 'vccq2' are not initialized, improving the robustness of the power management code. Signed-off-by: Alice Chao Reviewed-by: Peter Wang Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250811131423.3444014-11-peter.wang@mediatek.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 647b6eb05398..61c8fe135100 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -1629,6 +1629,9 @@ static void ufs_mtk_vccqx_set_lpm(struct ufs_hba *hba, bool lpm) { struct ufs_vreg *vccqx = NULL; + if (!hba->vreg_info.vccq && !hba->vreg_info.vccq2) + return; + if (hba->vreg_info.vccq) vccqx = hba->vreg_info.vccq; else