From 51231839d5ef007638bd1c3500e6a76b337a66f3 Mon Sep 17 00:00:00 2001 From: Udit Tiwari Date: Sun, 17 May 2026 16:22:33 +0530 Subject: [PATCH] crypto: qce - Add runtime PM and interconnect bandwidth scaling support The Qualcomm Crypto Engine (QCE) driver currently lacks support for runtime power management (PM) and interconnect bandwidth control. As a result, the hardware remains fully powered and clocks stay enabled even when the device is idle. Additionally, static interconnect bandwidth votes are held indefinitely, preventing the system from reclaiming unused bandwidth. Address this by enabling runtime PM and dynamic interconnect bandwidth scaling to allow the system to suspend the device when idle and scale interconnect usage based on actual demand. Improve overall system efficiency by reducing power usage and optimizing interconnect resource allocation. Signed-off-by: Udit Tiwari Tested-by: Pankaj Patil --- drivers/crypto/qce/core.c | 99 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 7 deletions(-) diff --git a/drivers/crypto/qce/core.c b/drivers/crypto/qce/core.c index ea5b9689ce68..abf9b4043aef 100644 --- a/drivers/crypto/qce/core.c +++ b/drivers/crypto/qce/core.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -87,7 +89,12 @@ static int qce_handle_queue(struct qce_device *qce, struct crypto_async_request *req) { struct crypto_async_request *async_req, *backlog; - int ret = 0, err; + int ret, err; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(qce->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; scoped_guard(mutex, &qce->lock) { if (req) @@ -206,23 +213,33 @@ static int qce_crypto_probe(struct platform_device *pdev) if (ret < 0) return ret; - qce->core = devm_clk_get_optional_enabled(qce->dev, "core"); + qce->core = devm_clk_get_optional(qce->dev, "core"); if (IS_ERR(qce->core)) return PTR_ERR(qce->core); - qce->iface = devm_clk_get_optional_enabled(qce->dev, "iface"); + qce->iface = devm_clk_get_optional(qce->dev, "iface"); if (IS_ERR(qce->iface)) return PTR_ERR(qce->iface); - qce->bus = devm_clk_get_optional_enabled(qce->dev, "bus"); + qce->bus = devm_clk_get_optional(qce->dev, "bus"); if (IS_ERR(qce->bus)) return PTR_ERR(qce->bus); - qce->mem_path = devm_of_icc_get(qce->dev, "memory"); + qce->mem_path = devm_of_icc_get(dev, "memory"); if (IS_ERR(qce->mem_path)) return PTR_ERR(qce->mem_path); - ret = icc_set_bw(qce->mem_path, QCE_DEFAULT_MEM_BANDWIDTH, QCE_DEFAULT_MEM_BANDWIDTH); + /* + * Enable runtime PM after clocks and ICC path are acquired so that + * the resume callback can enable clocks and apply the ICC bandwidth + * vote before any hardware access takes place. + */ + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) return ret; @@ -244,9 +261,76 @@ static int qce_crypto_probe(struct platform_device *pdev) qce->async_req_enqueue = qce_async_request_enqueue; qce->async_req_done = qce_async_request_done; - return devm_qce_register_algs(qce); + ret = devm_qce_register_algs(qce); + if (ret) + return ret; + + /* Configure autosuspend after successful init */ + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + + return 0; } +static int qce_runtime_suspend(struct device *dev) +{ + struct qce_device *qce = dev_get_drvdata(dev); + int ret; + + clk_disable_unprepare(qce->core); + clk_disable_unprepare(qce->iface); + clk_disable_unprepare(qce->bus); + + ret = icc_set_bw(qce->mem_path, 0, 0); + if (ret) { + clk_prepare_enable(qce->bus); + clk_prepare_enable(qce->iface); + clk_prepare_enable(qce->core); + return ret; + } + + return 0; +} + +static int qce_runtime_resume(struct device *dev) +{ + struct qce_device *qce = dev_get_drvdata(dev); + int ret; + + ret = icc_set_bw(qce->mem_path, QCE_DEFAULT_MEM_BANDWIDTH, + QCE_DEFAULT_MEM_BANDWIDTH); + if (ret) + return ret; + + ret = clk_prepare_enable(qce->core); + if (ret) + goto err_core; + + ret = clk_prepare_enable(qce->iface); + if (ret) + goto err_iface; + + ret = clk_prepare_enable(qce->bus); + if (ret) + goto err_bus; + + return 0; + +err_bus: + clk_disable_unprepare(qce->iface); +err_iface: + clk_disable_unprepare(qce->core); +err_core: + icc_set_bw(qce->mem_path, 0, 0); + return ret; +} + +static const struct dev_pm_ops qce_crypto_pm_ops = { + RUNTIME_PM_OPS(qce_runtime_suspend, qce_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + static const struct of_device_id qce_crypto_of_match[] = { { .compatible = "qcom,crypto-v5.1", }, { .compatible = "qcom,crypto-v5.4", }, @@ -260,6 +344,7 @@ static struct platform_driver qce_crypto_driver = { .driver = { .name = KBUILD_MODNAME, .of_match_table = qce_crypto_of_match, + .pm = pm_ptr(&qce_crypto_pm_ops), }, }; module_platform_driver(qce_crypto_driver);