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 <udit.tiwari@oss.qualcomm.com>
Tested-by: Pankaj Patil <pankaj.patil@oss.qualcomm.com>
This commit is contained in:
Udit Tiwari
2026-07-31 20:00:24 +02:00
committed by Konrad Dybcio
parent d43969b7f6
commit 51231839d5
+92 -7
View File
@@ -11,6 +11,8 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include <crypto/algapi.h>
#include <crypto/internal/hash.h>
@@ -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);