diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi index ecf4790f3415c..df5d1c75ed41c 100644 --- a/arch/arm64/boot/dts/qcom/kodiak.dtsi +++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi @@ -1087,6 +1087,27 @@ clock-names = "core", "iface"; power-domains = <&rpmhpd SC7280_CX>; + + operating-points-v2 = <&sdhc_ice_opp_table>; + + sdhc_ice_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-100000000 { + opp-hz = /bits/ 64 <100000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-150000000 { + opp-hz = /bits/ 64 <150000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + }; }; gpi_dma0: dma-controller@900000 { @@ -2597,6 +2618,27 @@ clock-names = "core", "iface"; power-domains = <&gcc GCC_UFS_PHY_GDSC>; + + operating-points-v2 = <&ice_opp_table>; + + ice_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-75000000 { + opp-hz = /bits/ 64 <75000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-150000000 { + opp-hz = /bits/ 64 <150000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + }; }; cryptobam: dma-controller@1dc4000 { diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi index 09a883bd26449..eb567a205ac41 100644 --- a/arch/arm64/boot/dts/qcom/monaco.dtsi +++ b/arch/arm64/boot/dts/qcom/monaco.dtsi @@ -2742,6 +2742,27 @@ clock-names = "core", "iface"; power-domains = <&gcc GCC_UFS_PHY_GDSC>; + + operating-points-v2 = <&ice_opp_table>; + + ice_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-75000000 { + opp-hz = /bits/ 64 <75000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-201600000 { + opp-hz = /bits/ 64 <201600000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-403200000 { + opp-hz = /bits/ 64 <403200000>; + required-opps = <&rpmhpd_opp_nom>; + }; + }; }; crypto: crypto@1dfa000 { @@ -4877,6 +4898,22 @@ clock-names = "core", "iface"; power-domains = <&rpmhpd RPMHPD_CX>; + + operating-points-v2 = <&sdhc_ice_opp_table>; + + sdhc_ice_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-150000000 { + opp-hz = /bits/ 64 <150000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + }; }; usb_1_hsphy: phy@8904000 { diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 8be178059aa81..09ef7912bd2d9 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1902,6 +1902,16 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops; /* forward decl */ +static int sdhci_msm_ice_scale_clk(struct sdhci_msm_host *msm_host, + unsigned long target_freq, + bool round_ceil) +{ + if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO) + return qcom_ice_scale_clk(msm_host->ice, target_freq, round_ceil); + + return 0; +} + static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, struct cqhci_host *cq_host) { @@ -1960,6 +1970,11 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, mmc->caps2 |= MMC_CAP2_CRYPTO; mmc->caps2 |= MMC_CAP2_CRYPTO_NO_REPROG; + + err = sdhci_msm_ice_scale_clk(msm_host, ULONG_MAX, false); + if (err && err != -EOPNOTSUPP) + dev_warn(dev, "Unable to boost ICE clock to TURBO\n"); + return 0; } @@ -2150,6 +2165,13 @@ sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host) { return 0; } + +static inline int +sdhci_msm_ice_scale_clk(struct sdhci_msm_host *msm_host, unsigned long target_freq, + bool round_ceil) +{ + return 0; +} #endif /* !CONFIG_MMC_CRYPTO */ /*****************************************************************************\ diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index 5f20108aa03eb..a7112b4ebab0c 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -113,6 +114,7 @@ struct qcom_ice { bool use_hwkm; bool hwkm_init_complete; u8 hwkm_version; + bool has_opp; }; static DEFINE_XARRAY(ice_handles); @@ -560,6 +562,50 @@ int qcom_ice_import_key(struct qcom_ice *ice, } EXPORT_SYMBOL_GPL(qcom_ice_import_key); +/** + * qcom_ice_scale_clk() - Scale ICE clock for DVFS-aware operations + * @ice: ICE driver data + * @target_freq: requested frequency in Hz + * @round_ceil: when true, selects nearest freq >= @target_freq; + * otherwise, selects nearest freq <= @target_freq + * + * Selects an OPP frequency based on @target_freq and the rounding direction + * specified by @round_ceil, then programs it using dev_pm_opp_set_rate(), + * including any voltage or power-domain transitions handled by the OPP + * framework. + * + * Return: 0 on success; -EOPNOTSUPP if no OPP table; or errno from + * dev_pm_opp_set_rate()/OPP lookup. + */ +int qcom_ice_scale_clk(struct qcom_ice *ice, unsigned long target_freq, + bool round_ceil) +{ + unsigned long ice_freq = target_freq; + struct dev_pm_opp *opp; + int ret; + + if (!ice->has_opp) + return -EOPNOTSUPP; + + if (round_ceil) + opp = dev_pm_opp_find_freq_ceil(ice->dev, &ice_freq); + else + opp = dev_pm_opp_find_freq_floor(ice->dev, &ice_freq); + + if (IS_ERR(opp)) + return PTR_ERR(opp); + dev_pm_opp_put(opp); + + ret = dev_pm_opp_set_rate(ice->dev, ice_freq); + if (ret) { + dev_err(ice->dev, "Unable to scale ICE clock rate\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_ice_scale_clk); + static struct qcom_ice *qcom_ice_create(struct device *dev, void __iomem *base) { @@ -738,6 +784,7 @@ static int qcom_ice_probe(struct platform_device *pdev) unsigned long phandle = pdev->dev.of_node->phandle; struct qcom_ice *engine; void __iomem *base; + int err; guard(mutex)(&ice_mutex); @@ -756,6 +803,34 @@ static int qcom_ice_probe(struct platform_device *pdev) return PTR_ERR(engine); } + err = devm_pm_opp_set_clkname(&pdev->dev, "core"); + if (err && err != -ENOENT) { + dev_err(&pdev->dev, "Unable to set core clkname to OPP-table\n"); + /* Store the error pointer for devm_of_qcom_ice_get() */ + xa_store(&ice_handles, phandle, ERR_PTR(err), GFP_KERNEL); + return err; + } + + /* OPP table is optional */ + err = devm_pm_opp_of_add_table(&pdev->dev); + if (err && err != -ENODEV) { + dev_err(&pdev->dev, "Invalid OPP table in Device tree\n"); + /* Store the error pointer for devm_of_qcom_ice_get() */ + xa_store(&ice_handles, phandle, ERR_PTR(err), GFP_KERNEL); + return err; + } + + /* + * The OPP table is optional. devm_pm_opp_of_add_table() returns + * -ENODEV when no OPP table is present in DT, which is not treated + * as an error. Therefore, track successful OPP registration only + * when err is not -ENODEV. + */ + if (err == -ENODEV) + dev_dbg(&pdev->dev, "ICE OPP table is not registered, please update your DT\n"); + else + engine->has_opp = true; + xa_store(&ice_handles, phandle, engine, GFP_KERNEL); return 0; diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index 291c434487648..dc15d7806c7b6 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -306,6 +306,15 @@ static int ufs_qcom_ice_prepare_key(struct blk_crypto_profile *profile, return qcom_ice_prepare_key(host->ice, lt_key, lt_key_size, eph_key); } +static int ufs_qcom_ice_scale_clk(struct ufs_qcom_host *host, unsigned long target_freq, + bool round_ceil) +{ + if (host->hba->caps & UFSHCD_CAP_CRYPTO) + return qcom_ice_scale_clk(host->ice, target_freq, round_ceil); + + return 0; +} + static const struct blk_crypto_ll_ops ufs_qcom_crypto_ops = { .keyslot_program = ufs_qcom_ice_keyslot_program, .keyslot_evict = ufs_qcom_ice_keyslot_evict, @@ -340,6 +349,12 @@ static void ufs_qcom_config_ice_allocator(struct ufs_qcom_host *host) { } +static int ufs_qcom_ice_scale_clk(struct ufs_qcom_host *host, unsigned long target_freq, + bool round_ceil) +{ + return 0; +} + #endif static void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host) @@ -1946,6 +1961,12 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba, bool scale_up, return err; } + err = ufs_qcom_ice_scale_clk(host, target_freq, !scale_up); + if (err && err != -EOPNOTSUPP) { + ufshcd_uic_hibern8_exit(hba); + return err; + } + ufs_qcom_icc_update_bw(host); ufshcd_uic_hibern8_exit(hba); } diff --git a/include/soc/qcom/ice.h b/include/soc/qcom/ice.h index 4bee553f0a59d..4eb58a264d416 100644 --- a/include/soc/qcom/ice.h +++ b/include/soc/qcom/ice.h @@ -30,5 +30,7 @@ int qcom_ice_import_key(struct qcom_ice *ice, const u8 *raw_key, size_t raw_key_size, u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]); struct qcom_ice *devm_of_qcom_ice_get(struct device *dev); +int qcom_ice_scale_clk(struct qcom_ice *ice, unsigned long target_freq, + bool round_ceil); #endif /* __QCOM_ICE_H__ */