[PATCH v12 1/6] soc: qcom: ice: Add OPP-based clock scaling support for ICE
Abhinaba Rakshit <[email protected]>
| Newsgroups | gmane.linux.scsi,gmane.linux.ports.arm.msm,gmane.linux.kernel,gmane.linux.kernel.mmc,gmane.linux.drivers.devicetree |
|---|---|
| Message-ID | <20260824-enable-ice-clock-scaling-v12-1-09271f583dc6@oss.qualcomm.com> |
Register optional operation-points-v2 table for ICE device during device probe. Attach the OPP-table with only the ICE core clock. Since, dtbinding is on a transition phase to include iface clock and clock-names, attaching the opp-table to core clock remains optional such that it does not cause probe failures. Introduce clock scaling API qcom_ice_scale_clk which scale ICE core clock based on the target frequency provided and if a valid OPP-table is registered. Use round_ceil passed to decide on the rounding of the clock freq against OPP-table. Clock scaling is disabled when a valid OPP-table is not registered. This ensures when an ICE-device specific OPP table is available, use the PM OPP framework to manage frequency scaling and maintain proper power-domain constraints. Reviewed-by: Harshal Dev <[email protected]> Signed-off-by: Abhinaba Rakshit <[email protected]> --- drivers/soc/qcom/ice.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/soc/qcom/ice.h | 2 ++ 2 files changed, 77 insertions(+) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index 5f20108aa03ebe9a47a10fba9afde420add0f34a..a7112b4ebab0c76ccb3faf9415dc22b28b73bdcc 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -17,6 +17,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/xarray.h> +#include <linux/pm_opp.h> #include <linux/firmware/qcom/qcom_scm.h> @@ -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/include/soc/qcom/ice.h b/include/soc/qcom/ice.h index 4bee553f0a59d86ec6ce20f7c7b4bce28a706415..4eb58a264d416e71228ed4b13e7f53c549261fdc 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__ */ -- 2.34.1