From 262893939604204d14d7621b6d2658199d1672bb Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Tue, 22 Jul 2025 11:07:16 +0800 Subject: [PATCH 1/9] scsi: ufs: host: mediatek: Simplify boolean conversion Simplify the conversion from unsigned int to boolean by removing explicit conversions and parentheses, relying on implicit conversion instead. This change ensures consistency with other usages in ufs-mediatek.c and streamlines the code. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250722030841.1998783-2-peter.wang@mediatek.com Reviewed-by: Chun-Hung Wu Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 182f58d0c9db..744efcde1fff 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -96,49 +96,49 @@ static bool ufs_mtk_is_boost_crypt_enabled(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); - return !!(host->caps & UFS_MTK_CAP_BOOST_CRYPT_ENGINE); + return host->caps & UFS_MTK_CAP_BOOST_CRYPT_ENGINE; } static bool ufs_mtk_is_va09_supported(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); - return !!(host->caps & UFS_MTK_CAP_VA09_PWR_CTRL); + return host->caps & UFS_MTK_CAP_VA09_PWR_CTRL; } static bool ufs_mtk_is_broken_vcc(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); - return !!(host->caps & UFS_MTK_CAP_BROKEN_VCC); + return host->caps & UFS_MTK_CAP_BROKEN_VCC; } static bool ufs_mtk_is_pmc_via_fastauto(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); - return !!(host->caps & UFS_MTK_CAP_PMC_VIA_FASTAUTO); + return host->caps & UFS_MTK_CAP_PMC_VIA_FASTAUTO; } static bool ufs_mtk_is_tx_skew_fix(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); - return (host->caps & UFS_MTK_CAP_TX_SKEW_FIX); + return host->caps & UFS_MTK_CAP_TX_SKEW_FIX; } static bool ufs_mtk_is_rtff_mtcmos(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); - return (host->caps & UFS_MTK_CAP_RTFF_MTCMOS); + return host->caps & UFS_MTK_CAP_RTFF_MTCMOS; } static bool ufs_mtk_is_allow_vccqx_lpm(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); - return (host->caps & UFS_MTK_CAP_ALLOW_VCCQX_LPM); + return host->caps & UFS_MTK_CAP_ALLOW_VCCQX_LPM; } static void ufs_mtk_cfg_unipro_cg(struct ufs_hba *hba, bool enable) From a84a9ba7888fabc00c9585a0626343dfd5538d59 Mon Sep 17 00:00:00 2001 From: Naomi Chu Date: Tue, 22 Jul 2025 11:07:17 +0800 Subject: [PATCH 2/9] scsi: ufs: host: mediatek: Add DDR_EN setting On MT6989 and later platforms, control of DDR_EN has been switched from SPM to EMI. To prevent abnormal access to DRAM, it is necessary to wait for 'ddren_ack' or assert 'ddren_urgent' after sending 'ddren_req'. Introduce the DDR_EN configuration in the UFS initialization flow, utilizing the assertion of 'ddren_urgent' to maintain performance. Signed-off-by: Naomi Chu Link: https://lore.kernel.org/r/20250722030841.1998783-3-peter.wang@mediatek.com Reviewed-by: Peter Wang Reviewed-by: Chun-Hung Wu Signed-off-by: Peter Wang Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 7 +++++++ drivers/ufs/host/ufs-mediatek.h | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 744efcde1fff..90351fff501c 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -267,6 +267,13 @@ static int ufs_mtk_hce_enable_notify(struct ufs_hba *hba, ufshcd_writel(hba, ufshcd_readl(hba, REG_UFS_XOUFS_CTRL) | 0x80, REG_UFS_XOUFS_CTRL); + + /* DDR_EN setting */ + if (host->ip_ver >= IP_VER_MT6989) { + ufshcd_rmwl(hba, UFS_MASK(0x7FFF, 8), + 0x453000, REG_UFS_MMIO_OPT_CTRL_0); + } + } return 0; diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h index 05d76a6bd772..1082f761bb44 100644 --- a/drivers/ufs/host/ufs-mediatek.h +++ b/drivers/ufs/host/ufs-mediatek.h @@ -192,4 +192,16 @@ struct ufs_mtk_host { /* MTK RTT support number */ #define MTK_MAX_NUM_RTT 2 +/* UFSHCI MTK ip version value */ +enum { + /* UFSHCI 3.1 */ + IP_VER_MT6878 = 0x10420200, + + /* UFSHCI 4.0 */ + IP_VER_MT6897 = 0x10440000, + IP_VER_MT6989 = 0x10450000, + + IP_VER_NONE = 0xFFFFFFFF +}; + #endif /* !_UFS_MEDIATEK_H */ From 16b30c7a4c564e80fefe7e6416320f4f5b776d60 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Tue, 22 Jul 2025 11:07:18 +0800 Subject: [PATCH 3/9] scsi: ufs: host: mediatek: Change ref-clk timeout policy Update the timeout policy for ref-clk control. - If a clock-on operation times out, it is assumed that the clock is off. The system will notify TFA to perform clock-off settings. - If a clock-off operation times out, it is assumed that the clock will eventually turn off. The 'ref_clk_enabled' flag is set directly. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250722030841.1998783-4-peter.wang@mediatek.com Reviewed-by: Chun-Hung Wu Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 90351fff501c..b30203d83ef1 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -351,7 +351,16 @@ static int ufs_mtk_setup_ref_clk(struct ufs_hba *hba, bool on) dev_err(hba->dev, "missing ack of refclk req, reg: 0x%x\n", value); - ufs_mtk_ref_clk_notify(host->ref_clk_enabled, POST_CHANGE, res); + /* + * If clock on timeout, assume clock is off, notify tfa do clock + * off setting.(keep DIFN disable, release resource) + * If clock off timeout, assume clock will off finally, + * set ref_clk_enabled directly.(keep DIFN disable, keep resource) + */ + if (on) + ufs_mtk_ref_clk_notify(false, POST_CHANGE, res); + else + host->ref_clk_enabled = false; return -ETIMEDOUT; From a44ff97f895bd8615ebb53e6e199b74152c18bba Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Tue, 22 Jul 2025 11:07:19 +0800 Subject: [PATCH 4/9] scsi: ufs: host: mediatek: Handle broken RTC based on DTS setting Introduce a mechanism to handle broken RTC by checking the DTS setting. The configuration is specifically required for legacy platform. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250722030841.1998783-5-peter.wang@mediatek.com Reviewed-by: Chun-Hung Wu Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 8 +++++++- drivers/ufs/host/ufs-mediatek.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index b30203d83ef1..112056e5d8e0 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -679,6 +679,9 @@ static void ufs_mtk_init_host_caps(struct ufs_hba *hba) if (of_property_read_bool(np, "mediatek,ufs-rtff-mtcmos")) host->caps |= UFS_MTK_CAP_RTFF_MTCMOS; + if (of_property_read_bool(np, "mediatek,ufs-broken-rtc")) + host->caps |= UFS_MTK_CAP_MCQ_BROKEN_RTC; + dev_info(hba->dev, "caps: 0x%x", host->caps); } @@ -1035,8 +1038,11 @@ static int ufs_mtk_init(struct ufs_hba *hba) shost->rpm_autosuspend_delay = MTK_RPM_AUTOSUSPEND_DELAY_MS; hba->quirks |= UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL; + hba->quirks |= UFSHCD_QUIRK_MCQ_BROKEN_INTR; - hba->quirks |= UFSHCD_QUIRK_MCQ_BROKEN_RTC; + if (host->caps & UFS_MTK_CAP_MCQ_BROKEN_RTC) + hba->quirks |= UFSHCD_QUIRK_MCQ_BROKEN_RTC; + hba->vps->wb_flush_threshold = UFS_WB_BUF_REMAIN_PERCENT(80); if (host->caps & UFS_MTK_CAP_DISABLE_AH8) diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h index 1082f761bb44..abb4a4fd4402 100644 --- a/drivers/ufs/host/ufs-mediatek.h +++ b/drivers/ufs/host/ufs-mediatek.h @@ -133,6 +133,8 @@ enum ufs_mtk_host_caps { UFS_MTK_CAP_DISABLE_MCQ = 1 << 8, /* Control MTCMOS with RTFF */ UFS_MTK_CAP_RTFF_MTCMOS = 1 << 9, + + UFS_MTK_CAP_MCQ_BROKEN_RTC = 1 << 10, }; struct ufs_mtk_crypt_cfg { From 66e26a4b8a7793137551e77a7e9f6eb1263a49c2 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Tue, 22 Jul 2025 11:07:20 +0800 Subject: [PATCH 5/9] scsi: ufs: host: mediatek: Set IRQ affinity policy for MCQ mode Set the IRQ affinity for MCQ mode to improve performance. Specifically, it migrates the IRQ from CPU0 to CPU3 to enhance IRQ handling efficiency. Setting IRQ affinity directly from the kernel allows the configuration to take effect earlier, and provides greater security and consistency, especially important for systems with strict performanceor real-time requirements. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250722030841.1998783-6-peter.wang@mediatek.com Reviewed-by: Chun-Hung Wu Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 112056e5d8e0..78eaf057cdc3 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -798,6 +798,46 @@ static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on, return ret; } +static u32 ufs_mtk_mcq_get_irq(struct ufs_hba *hba, unsigned int cpu) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + struct blk_mq_tag_set *tag_set = &hba->host->tag_set; + struct blk_mq_queue_map *map = &tag_set->map[HCTX_TYPE_DEFAULT]; + unsigned int nr = map->nr_queues; + unsigned int q_index; + + q_index = map->mq_map[cpu]; + if (q_index > nr) { + dev_err(hba->dev, "hwq index %d exceed %d\n", + q_index, nr); + return MTK_MCQ_INVALID_IRQ; + } + + return host->mcq_intr_info[q_index].irq; +} + +static void ufs_mtk_mcq_set_irq_affinity(struct ufs_hba *hba, unsigned int cpu) +{ + unsigned int irq, _cpu; + int ret; + + irq = ufs_mtk_mcq_get_irq(hba, cpu); + if (irq == MTK_MCQ_INVALID_IRQ) { + dev_err(hba->dev, "invalid irq. unable to bind irq to cpu%d", cpu); + return; + } + + /* force migrate irq of cpu0 to cpu3 */ + _cpu = (cpu == 0) ? 3 : cpu; + ret = irq_set_affinity(irq, cpumask_of(_cpu)); + if (ret) { + dev_err(hba->dev, "set irq %d affinity to CPU %d failed\n", + irq, _cpu); + return; + } + dev_info(hba->dev, "set irq %d affinity to CPU: %d\n", irq, _cpu); +} + static void ufs_mtk_get_controller_version(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); @@ -1527,6 +1567,13 @@ static int ufs_mtk_apply_dev_quirks(struct ufs_hba *hba) { struct ufs_dev_info *dev_info = &hba->dev_info; u16 mid = dev_info->wmanufacturerid; + unsigned int cpu; + + if (hba->mcq_enabled) { + /* Iterate all cpus to set affinity for mcq irqs */ + for (cpu = 0; cpu < nr_cpu_ids; cpu++) + ufs_mtk_mcq_set_irq_affinity(hba, cpu); + } if (mid == UFS_VENDOR_SAMSUNG) { ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE), 6); From 7996746394df569355113ce4643ab892442cfe1d Mon Sep 17 00:00:00 2001 From: Alice Chao Date: Tue, 22 Jul 2025 11:07:21 +0800 Subject: [PATCH 6/9] scsi: ufs: host: mediatek: Add more UFSCHI hardware versions Introduce a function for version control to distinguish between new and old platforms. Update the handling of hardware IP versions, ensuring correct version comparisons by adjusting the version format for specific projects. Signed-off-by: Alice Chao Link: https://lore.kernel.org/r/20250722030841.1998783-7-peter.wang@mediatek.com Reviewed-by: Peter Wang Reviewed-by: Chun-Hung Wu Signed-off-by: Peter Wang Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 47 ++++++++++++++++++++++++++++++++- drivers/ufs/host/ufs-mediatek.h | 12 +++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 78eaf057cdc3..28aba44068da 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -838,6 +838,51 @@ static void ufs_mtk_mcq_set_irq_affinity(struct ufs_hba *hba, unsigned int cpu) dev_info(hba->dev, "set irq %d affinity to CPU: %d\n", irq, _cpu); } +static bool ufs_mtk_is_legacy_chipset(struct ufs_hba *hba, u32 hw_ip_ver) +{ + bool is_legacy = false; + + switch (hw_ip_ver) { + case IP_LEGACY_VER_MT6893: + case IP_LEGACY_VER_MT6781: + /* can add other legacy chipset ID here accordingly */ + is_legacy = true; + break; + default: + break; + } + dev_info(hba->dev, "legacy IP version - 0x%x, is legacy : %d", hw_ip_ver, is_legacy); + + return is_legacy; +} + +/* + * HW version format has been changed from 01MMmmmm to 1MMMmmmm, since + * project MT6878. In order to perform correct version comparison, + * version number is changed by SW for the following projects. + * IP_VER_MT6983 0x00360000 to 0x10360000 + * IP_VER_MT6897 0x01440000 to 0x10440000 + * IP_VER_MT6989 0x01450000 to 0x10450000 + * IP_VER_MT6991 0x01460000 to 0x10460000 + */ +static void ufs_mtk_get_hw_ip_version(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + u32 hw_ip_ver; + + hw_ip_ver = ufshcd_readl(hba, REG_UFS_MTK_IP_VER); + + if (((hw_ip_ver & (0xFF << 24)) == (0x1 << 24)) || + ((hw_ip_ver & (0xFF << 24)) == 0)) { + hw_ip_ver &= ~(0xFF << 24); + hw_ip_ver |= (0x1 << 28); + } + + host->ip_ver = hw_ip_ver; + + host->legacy_ip_ver = ufs_mtk_is_legacy_chipset(hba, hw_ip_ver); +} + static void ufs_mtk_get_controller_version(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); @@ -1112,7 +1157,7 @@ static int ufs_mtk_init(struct ufs_hba *hba) ufs_mtk_setup_clocks(hba, true, POST_CHANGE); - host->ip_ver = ufshcd_readl(hba, REG_UFS_MTK_IP_VER); + ufs_mtk_get_hw_ip_version(hba); goto out; diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h index abb4a4fd4402..fd229514384e 100644 --- a/drivers/ufs/host/ufs-mediatek.h +++ b/drivers/ufs/host/ufs-mediatek.h @@ -181,6 +181,7 @@ struct ufs_mtk_host { u16 ref_clk_ungating_wait_us; u16 ref_clk_gating_wait_us; u32 ip_ver; + bool legacy_ip_ver; bool mcq_set_intr; bool is_mcq_intr_enabled; @@ -197,13 +198,24 @@ struct ufs_mtk_host { /* UFSHCI MTK ip version value */ enum { /* UFSHCI 3.1 */ + IP_VER_MT6983 = 0x10360000, IP_VER_MT6878 = 0x10420200, /* UFSHCI 4.0 */ IP_VER_MT6897 = 0x10440000, IP_VER_MT6989 = 0x10450000, + IP_VER_MT6899 = 0x10450100, + IP_VER_MT6991_A0 = 0x10460000, + IP_VER_MT6991_B0 = 0x10470000, + IP_VER_MT6993 = 0x10480000, IP_VER_NONE = 0xFFFFFFFF }; +enum ip_ver_legacy { + IP_LEGACY_VER_MT6781 = 0x10380000, + IP_LEGACY_VER_MT6879 = 0x10360000, + IP_LEGACY_VER_MT6893 = 0x20160706 +}; + #endif /* !_UFS_MEDIATEK_H */ From ff40f31216fffc1b7f7e5a9e27a317a29a798289 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Tue, 22 Jul 2025 11:07:22 +0800 Subject: [PATCH 7/9] scsi: ufs: host: mediatek: Add clock scaling query function Introduce a clock scaling readiness query function to streamline the process of checking clock scaling parameters. This function simplifies the code by encapsulating the logic for determining if clock scaling is ready. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250722030841.1998783-8-peter.wang@mediatek.com Reviewed-by: Chun-Hung Wu Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 28aba44068da..0b3cce8d9787 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -141,6 +141,16 @@ static bool ufs_mtk_is_allow_vccqx_lpm(struct ufs_hba *hba) return host->caps & UFS_MTK_CAP_ALLOW_VCCQX_LPM; } +static bool ufs_mtk_is_clk_scale_ready(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + struct ufs_mtk_clk *mclk = &host->mclk; + + return mclk->ufs_sel_clki && + mclk->ufs_sel_max_clki && + mclk->ufs_sel_min_clki; +} + static void ufs_mtk_cfg_unipro_cg(struct ufs_hba *hba, bool enable) { u32 tmp; @@ -922,7 +932,6 @@ static void ufs_mtk_init_clocks(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); struct list_head *head = &hba->clk_list_head; - struct ufs_mtk_clk *mclk = &host->mclk; struct ufs_clk_info *clki, *clki_tmp; /* @@ -944,8 +953,7 @@ static void ufs_mtk_init_clocks(struct ufs_hba *hba) } } - if (!mclk->ufs_sel_clki || !mclk->ufs_sel_max_clki || - !mclk->ufs_sel_min_clki) { + if (!ufs_mtk_is_clk_scale_ready(hba)) { hba->caps &= ~UFSHCD_CAP_CLK_SCALING; dev_info(hba->dev, "%s: Clk-scaling not ready. Feature disabled.", From 31a20e9f7c766896fbfea45897969bfd1490b466 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Tue, 22 Jul 2025 11:07:23 +0800 Subject: [PATCH 8/9] scsi: ufs: host: mediatek: Support clock scaling with Vcore binding Add support for clock scaling with Vcore binding: 1. Parse the DTS setting for Vcore voltage. 2. Set the Vcore voltage to the DTS-specified value before scaling up. 3. Reset the Vcore voltage to the default setting after scaling down. These changes ensure that the Vcore voltage is appropriately managed during clock scaling operations to maintain system stability and performance. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250722030841.1998783-9-peter.wang@mediatek.com Reviewed-by: Chun-Hung Wu Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 129 +++++++++++++++++++++++++++----- drivers/ufs/host/ufs-mediatek.h | 3 + 2 files changed, 112 insertions(+), 20 deletions(-) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 0b3cce8d9787..a0c53d796a60 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -933,6 +933,9 @@ static void ufs_mtk_init_clocks(struct ufs_hba *hba) struct ufs_mtk_host *host = ufshcd_get_variant(hba); struct list_head *head = &hba->clk_list_head; struct ufs_clk_info *clki, *clki_tmp; + struct device *dev = hba->dev; + struct regulator *reg; + u32 volt; /* * Find private clocks and store them in struct ufs_mtk_clk. @@ -958,6 +961,35 @@ static void ufs_mtk_init_clocks(struct ufs_hba *hba) dev_info(hba->dev, "%s: Clk-scaling not ready. Feature disabled.", __func__); + return; + } + + /* + * Default get vcore if dts have these settings. + * No matter clock scaling support or not. (may disable by customer) + */ + reg = devm_regulator_get_optional(dev, "dvfsrc-vcore"); + if (IS_ERR(reg)) { + dev_info(dev, "failed to get dvfsrc-vcore: %ld", + PTR_ERR(reg)); + return; + } + + if (of_property_read_u32(dev->of_node, "clk-scale-up-vcore-min", + &volt)) { + dev_info(dev, "failed to get clk-scale-up-vcore-min"); + return; + } + + host->mclk.reg_vcore = reg; + host->mclk.vcore_volt = volt; + + /* If default boot is max gear, request vcore */ + if (reg && volt && host->clk_scale_up) { + if (regulator_set_voltage(reg, volt, INT_MAX)) { + dev_info(hba->dev, + "Failed to set vcore to %d\n", volt); + } } } @@ -1126,6 +1158,7 @@ static int ufs_mtk_init(struct ufs_hba *hba) /* Enable clk scaling*/ hba->caps |= UFSHCD_CAP_CLK_SCALING; + host->clk_scale_up = true; /* default is max freq */ /* Set runtime pm delay to replace default */ shost->rpm_autosuspend_delay = MTK_RPM_AUTOSUSPEND_DELAY_MS; @@ -1720,6 +1753,69 @@ static void ufs_mtk_config_scaling_param(struct ufs_hba *hba, hba->vps->ondemand_data.downdifferential = 20; } +static void _ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + struct ufs_mtk_clk *mclk = &host->mclk; + struct ufs_clk_info *clki = mclk->ufs_sel_clki; + struct regulator *reg; + int volt, ret = 0; + bool clk_bind_vcore = false; + + if (!hba->clk_scaling.is_initialized) + return; + + if (!clki) + return; + + reg = host->mclk.reg_vcore; + volt = host->mclk.vcore_volt; + if (reg && volt != 0) + clk_bind_vcore = true; + + ret = clk_prepare_enable(clki->clk); + if (ret) { + dev_info(hba->dev, + "clk_prepare_enable() fail, ret: %d\n", ret); + return; + } + + if (scale_up) { + if (clk_bind_vcore) { + ret = regulator_set_voltage(reg, volt, INT_MAX); + if (ret) { + dev_info(hba->dev, + "Failed to set vcore to %d\n", volt); + goto out; + } + } + + ret = clk_set_parent(clki->clk, mclk->ufs_sel_max_clki->clk); + if (ret) { + dev_info(hba->dev, "Failed to set clk mux, ret = %d\n", + ret); + } + } else { + ret = clk_set_parent(clki->clk, mclk->ufs_sel_min_clki->clk); + if (ret) { + dev_info(hba->dev, "Failed to set clk mux, ret = %d\n", + ret); + goto out; + } + + if (clk_bind_vcore) { + ret = regulator_set_voltage(reg, 0, INT_MAX); + if (ret) { + dev_info(hba->dev, + "failed to set vcore to MIN\n"); + } + } + } + +out: + clk_disable_unprepare(clki->clk); +} + /** * ufs_mtk_clk_scale - Internal clk scaling operation * @@ -1737,30 +1833,23 @@ static void ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up) struct ufs_mtk_host *host = ufshcd_get_variant(hba); struct ufs_mtk_clk *mclk = &host->mclk; struct ufs_clk_info *clki = mclk->ufs_sel_clki; - int ret = 0; - ret = clk_prepare_enable(clki->clk); - if (ret) { - dev_info(hba->dev, - "clk_prepare_enable() fail, ret: %d\n", ret); - return; - } + if (host->clk_scale_up == scale_up) + goto out; - if (scale_up) { - ret = clk_set_parent(clki->clk, mclk->ufs_sel_max_clki->clk); + if (scale_up) + _ufs_mtk_clk_scale(hba, true); + else + _ufs_mtk_clk_scale(hba, false); + + host->clk_scale_up = scale_up; + + /* Must always set before clk_set_rate() */ + if (scale_up) clki->curr_freq = clki->max_freq; - } else { - ret = clk_set_parent(clki->clk, mclk->ufs_sel_min_clki->clk); + else clki->curr_freq = clki->min_freq; - } - - if (ret) { - dev_info(hba->dev, - "Failed to set ufs_sel_clki, ret: %d\n", ret); - } - - clk_disable_unprepare(clki->clk); - +out: trace_ufs_mtk_clk_scale(clki->name, scale_up, clk_get_rate(clki->clk)); } diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h index fd229514384e..0b25ce5aa836 100644 --- a/drivers/ufs/host/ufs-mediatek.h +++ b/drivers/ufs/host/ufs-mediatek.h @@ -149,6 +149,8 @@ struct ufs_mtk_clk { struct ufs_clk_info *ufs_sel_clki; /* Mux */ struct ufs_clk_info *ufs_sel_max_clki; /* Max src */ struct ufs_clk_info *ufs_sel_min_clki; /* Min src */ + struct regulator *reg_vcore; + int vcore_volt; }; struct ufs_mtk_hw_ver { @@ -178,6 +180,7 @@ struct ufs_mtk_host { bool mphy_powered_on; bool unipro_lpm; bool ref_clk_enabled; + bool clk_scale_up; u16 ref_clk_ungating_wait_us; u16 ref_clk_gating_wait_us; u32 ip_ver; From 5e5976f5242de61b9c09c32795b3d7b90364af51 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Tue, 22 Jul 2025 11:07:24 +0800 Subject: [PATCH 9/9] scsi: ufs: host: mediatek: Support FDE (AES) clock scaling Add support for scaling the FDE (AES) clock to achieve higher performance, particularly for HS-G5: 1. Parse DTS settings for FDE min/max mux. 2. Scale up the FDE clock when required for enhanced performance. These changes ensure that the FDE clock can be dynamically adjusted based on performance needs, leveraging DTS configurations. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20250722030841.1998783-10-peter.wang@mediatek.com Reviewed-by: Chun-Hung Wu Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-mediatek.c | 54 ++++++++++++++++++++++++++++++++- drivers/ufs/host/ufs-mediatek.h | 3 ++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index a0c53d796a60..91a2f3428b9f 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -953,9 +953,23 @@ static void ufs_mtk_init_clocks(struct ufs_hba *hba) host->mclk.ufs_sel_min_clki = clki; clk_disable_unprepare(clki->clk); list_del(&clki->list); + } else if (!strcmp(clki->name, "ufs_fde")) { + host->mclk.ufs_fde_clki = clki; + } else if (!strcmp(clki->name, "ufs_fde_max_src")) { + host->mclk.ufs_fde_max_clki = clki; + clk_disable_unprepare(clki->clk); + list_del(&clki->list); + } else if (!strcmp(clki->name, "ufs_fde_min_src")) { + host->mclk.ufs_fde_min_clki = clki; + clk_disable_unprepare(clki->clk); + list_del(&clki->list); } } + list_for_each_entry(clki, head, list) { + dev_info(hba->dev, "clk \"%s\" present", clki->name); + } + if (!ufs_mtk_is_clk_scale_ready(hba)) { hba->caps &= ~UFSHCD_CAP_CLK_SCALING; dev_info(hba->dev, @@ -1758,14 +1772,16 @@ static void _ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up) struct ufs_mtk_host *host = ufshcd_get_variant(hba); struct ufs_mtk_clk *mclk = &host->mclk; struct ufs_clk_info *clki = mclk->ufs_sel_clki; + struct ufs_clk_info *fde_clki = mclk->ufs_fde_clki; struct regulator *reg; int volt, ret = 0; bool clk_bind_vcore = false; + bool clk_fde_scale = false; if (!hba->clk_scaling.is_initialized) return; - if (!clki) + if (!clki || !fde_clki) return; reg = host->mclk.reg_vcore; @@ -1773,6 +1789,9 @@ static void _ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up) if (reg && volt != 0) clk_bind_vcore = true; + if (mclk->ufs_fde_max_clki && mclk->ufs_fde_min_clki) + clk_fde_scale = true; + ret = clk_prepare_enable(clki->clk); if (ret) { dev_info(hba->dev, @@ -1780,6 +1799,15 @@ static void _ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up) return; } + if (clk_fde_scale) { + ret = clk_prepare_enable(fde_clki->clk); + if (ret) { + dev_info(hba->dev, + "fde clk_prepare_enable() fail, ret: %d\n", ret); + return; + } + } + if (scale_up) { if (clk_bind_vcore) { ret = regulator_set_voltage(reg, volt, INT_MAX); @@ -1795,7 +1823,28 @@ static void _ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up) dev_info(hba->dev, "Failed to set clk mux, ret = %d\n", ret); } + + if (clk_fde_scale) { + ret = clk_set_parent(fde_clki->clk, + mclk->ufs_fde_max_clki->clk); + if (ret) { + dev_info(hba->dev, + "Failed to set fde clk mux, ret = %d\n", + ret); + } + } } else { + if (clk_fde_scale) { + ret = clk_set_parent(fde_clki->clk, + mclk->ufs_fde_min_clki->clk); + if (ret) { + dev_info(hba->dev, + "Failed to set fde clk mux, ret = %d\n", + ret); + goto out; + } + } + ret = clk_set_parent(clki->clk, mclk->ufs_sel_min_clki->clk); if (ret) { dev_info(hba->dev, "Failed to set clk mux, ret = %d\n", @@ -1814,6 +1863,9 @@ static void _ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up) out: clk_disable_unprepare(clki->clk); + + if (clk_fde_scale) + clk_disable_unprepare(fde_clki->clk); } /** diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h index 0b25ce5aa836..e46dc5fa209d 100644 --- a/drivers/ufs/host/ufs-mediatek.h +++ b/drivers/ufs/host/ufs-mediatek.h @@ -149,6 +149,9 @@ struct ufs_mtk_clk { struct ufs_clk_info *ufs_sel_clki; /* Mux */ struct ufs_clk_info *ufs_sel_max_clki; /* Max src */ struct ufs_clk_info *ufs_sel_min_clki; /* Min src */ + struct ufs_clk_info *ufs_fde_clki; /* Mux */ + struct ufs_clk_info *ufs_fde_max_clki; /* Max src */ + struct ufs_clk_info *ufs_fde_min_clki; /* Min src */ struct regulator *reg_vcore; int vcore_volt; };