Skip to content

Commit fc2cab8

Browse files
committed
FROMLIST: soc: qcom: ice: Add OPP-based clock scaling support for ICE
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 trasition phase to include iface clock and clock-names, attaching the opp-table to core clock remains options 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. Also, ensure to drop the votes in suspend to prevent power/thermal retention. Subsequently restore the frequency in resume from core_clk_freq which stores the last ICE core clock operating frequency. Link: https://lore.kernel.org/all/20260409-enable-ice-clock-scaling-v8-1-ca1129798606@oss.qualcomm.com/ Signed-off-by: Abhinaba Rakshit <abhinaba.rakshit@oss.qualcomm.com>
1 parent 0a86a68 commit fc2cab8

2 files changed

Lines changed: 94 additions & 0 deletions

File tree

drivers/soc/qcom/ice.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/of.h>
1717
#include <linux/of_platform.h>
1818
#include <linux/platform_device.h>
19+
#include <linux/pm_opp.h>
1920

2021
#include <linux/firmware/qcom/qcom_scm.h>
2122

@@ -112,6 +113,8 @@ struct qcom_ice {
112113
bool use_hwkm;
113114
bool hwkm_init_complete;
114115
u8 hwkm_version;
116+
unsigned long core_clk_freq;
117+
bool has_opp;
115118
};
116119

117120
static bool qcom_ice_check_supported(struct qcom_ice *ice)
@@ -311,6 +314,10 @@ int qcom_ice_resume(struct qcom_ice *ice)
311314
struct device *dev = ice->dev;
312315
int err;
313316

317+
/* Restore the ICE core clk freq */
318+
if (ice->has_opp && ice->core_clk_freq)
319+
dev_pm_opp_set_rate(ice->dev, ice->core_clk_freq);
320+
314321
err = clk_prepare_enable(ice->core_clk);
315322
if (err) {
316323
dev_err(dev, "Failed to enable core clock: %d\n", err);
@@ -331,6 +338,11 @@ int qcom_ice_suspend(struct qcom_ice *ice)
331338
{
332339
clk_disable_unprepare(ice->iface_clk);
333340
clk_disable_unprepare(ice->core_clk);
341+
342+
/* Drop the clock votes while suspend */
343+
if (ice->has_opp)
344+
dev_pm_opp_set_rate(ice->dev, 0);
345+
334346
ice->hwkm_init_complete = false;
335347

336348
return 0;
@@ -556,6 +568,51 @@ int qcom_ice_import_key(struct qcom_ice *ice,
556568
}
557569
EXPORT_SYMBOL_GPL(qcom_ice_import_key);
558570

571+
/**
572+
* qcom_ice_scale_clk() - Scale ICE clock for DVFS-aware operations
573+
* @ice: ICE driver data
574+
* @target_freq: requested frequency in Hz
575+
* @round_ceil: when true, selects nearest freq >= @target_freq;
576+
* otherwise, selects nearest freq <= @target_freq
577+
*
578+
* Selects an OPP frequency based on @target_freq and the rounding direction
579+
* specified by @round_ceil, then programs it using dev_pm_opp_set_rate(),
580+
* including any voltage or power-domain transitions handled by the OPP
581+
* framework. Updates ice->core_clk_freq on success.
582+
*
583+
* Return: 0 on success; -EOPNOTSUPP if no OPP table; or error from
584+
* dev_pm_opp_set_rate()/OPP lookup.
585+
*/
586+
int qcom_ice_scale_clk(struct qcom_ice *ice, unsigned long target_freq,
587+
bool round_ceil)
588+
{
589+
unsigned long ice_freq = target_freq;
590+
struct dev_pm_opp *opp;
591+
int ret;
592+
593+
if (!ice->has_opp)
594+
return -EOPNOTSUPP;
595+
596+
if (round_ceil)
597+
opp = dev_pm_opp_find_freq_ceil(ice->dev, &ice_freq);
598+
else
599+
opp = dev_pm_opp_find_freq_floor(ice->dev, &ice_freq);
600+
601+
if (IS_ERR(opp))
602+
return PTR_ERR(opp);
603+
dev_pm_opp_put(opp);
604+
605+
ret = dev_pm_opp_set_rate(ice->dev, ice_freq);
606+
if (ret) {
607+
dev_err(ice->dev, "Unable to scale ICE clock rate\n");
608+
return ret;
609+
}
610+
ice->core_clk_freq = ice_freq;
611+
612+
return ret;
613+
}
614+
EXPORT_SYMBOL_GPL(qcom_ice_scale_clk);
615+
559616
static struct qcom_ice *qcom_ice_create(struct device *dev,
560617
void __iomem *base)
561618
{
@@ -731,6 +788,7 @@ static int qcom_ice_probe(struct platform_device *pdev)
731788
{
732789
struct qcom_ice *engine;
733790
void __iomem *base;
791+
int err;
734792

735793
base = devm_platform_ioremap_resource(pdev, 0);
736794
if (IS_ERR(base)) {
@@ -742,6 +800,40 @@ static int qcom_ice_probe(struct platform_device *pdev)
742800
if (IS_ERR(engine))
743801
return PTR_ERR(engine);
744802

803+
/* qcom_ice_create() may return NULL if scm calls are not available */
804+
if (!engine)
805+
return -EOPNOTSUPP;
806+
807+
err = devm_pm_opp_set_clkname(&pdev->dev, "core");
808+
if (err && err != -ENOENT) {
809+
dev_err(&pdev->dev, "Unable to set core clkname to OPP-table\n");
810+
return err;
811+
}
812+
813+
/* OPP table is optional */
814+
err = devm_pm_opp_of_add_table(&pdev->dev);
815+
if (err && err != -ENODEV) {
816+
dev_err(&pdev->dev, "Invalid OPP table in Device tree\n");
817+
return err;
818+
}
819+
820+
/*
821+
* The OPP table is optional. devm_pm_opp_of_add_table() returns
822+
* -ENODEV when no OPP table is present in DT, which is not treated
823+
* as an error. Therefore, track successful OPP registration only
824+
* when the return value is 0.
825+
*/
826+
engine->has_opp = (err == 0);
827+
if (!engine->has_opp)
828+
dev_info(&pdev->dev, "ICE OPP table is not registered, please update your DT\n");
829+
830+
/*
831+
* Store the core clock rate for suspend resume cycles,
832+
* against OPP aware DVFS operations. core_clk_freq will
833+
* have a valid value only for non-legacy bindings.
834+
*/
835+
engine->core_clk_freq = clk_get_rate(engine->core_clk);
836+
745837
platform_set_drvdata(pdev, engine);
746838

747839
return 0;

include/soc/qcom/ice.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,7 @@ int qcom_ice_import_key(struct qcom_ice *ice,
3030
const u8 *raw_key, size_t raw_key_size,
3131
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);
3232
struct qcom_ice *devm_of_qcom_ice_get(struct device *dev);
33+
int qcom_ice_scale_clk(struct qcom_ice *ice, unsigned long target_freq,
34+
bool round_ceil);
3335

3436
#endif /* __QCOM_ICE_H__ */

0 commit comments

Comments
 (0)