summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSibi Sankar <quic_sibis@quicinc.com>2022-05-23 12:30:57 +0530
committerBjorn Andersson <bjorn.andersson@linaro.org>2022-06-29 21:48:32 -0500
commit65b7ebda5028615f7ee7628759e05da5db7f0d25 (patch)
treec4f5b5412003947c9ed8280037bbfc6dd3afdf18
parent7f045132bc23904fa962d9e1574dca521b9d47ec (diff)
downloadlinux-65b7ebda5028615f7ee7628759e05da5db7f0d25.tar.gz
linux-65b7ebda5028615f7ee7628759e05da5db7f0d25.tar.bz2
linux-65b7ebda5028615f7ee7628759e05da5db7f0d25.zip
firmware: qcom_scm: Add bw voting support to the SCM interface
The SMC calls required by remoteproc PAS driver on SM8450 SoCs get a performance benefit from having a max vote to the crypto->ddr path. Add support for bandwidth (bw) voting for those SMC calls when the interconnects property is specified. Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> Link: https://lore.kernel.org/r/1653289258-17699-3-git-send-email-quic_sibis@quicinc.com
-rw-r--r--drivers/firmware/qcom_scm.c69
1 files changed, 69 insertions, 0 deletions
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index 4623d2919ed0..cdbfe54c8146 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -7,6 +7,7 @@
#include <linux/cpumask.h>
#include <linux/export.h>
#include <linux/dma-mapping.h>
+#include <linux/interconnect.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/qcom_scm.h>
@@ -31,8 +32,13 @@ struct qcom_scm {
struct clk *core_clk;
struct clk *iface_clk;
struct clk *bus_clk;
+ struct icc_path *path;
struct reset_controller_dev reset;
+ /* control access to the interconnect path */
+ struct mutex scm_bw_lock;
+ int scm_vote_count;
+
u64 dload_mode_addr;
};
@@ -99,6 +105,42 @@ static void qcom_scm_clk_disable(void)
clk_disable_unprepare(__scm->bus_clk);
}
+static int qcom_scm_bw_enable(void)
+{
+ int ret = 0;
+
+ if (!__scm->path)
+ return 0;
+
+ if (IS_ERR(__scm->path))
+ return -EINVAL;
+
+ mutex_lock(&__scm->scm_bw_lock);
+ if (!__scm->scm_vote_count) {
+ ret = icc_set_bw(__scm->path, 0, UINT_MAX);
+ if (ret < 0) {
+ dev_err(__scm->dev, "failed to set bandwidth request\n");
+ goto err_bw;
+ }
+ }
+ __scm->scm_vote_count++;
+err_bw:
+ mutex_unlock(&__scm->scm_bw_lock);
+
+ return ret;
+}
+
+static void qcom_scm_bw_disable(void)
+{
+ if (IS_ERR_OR_NULL(__scm->path))
+ return;
+
+ mutex_lock(&__scm->scm_bw_lock);
+ if (__scm->scm_vote_count-- == 1)
+ icc_set_bw(__scm->path, 0, 0);
+ mutex_unlock(&__scm->scm_bw_lock);
+}
+
enum qcom_scm_convention qcom_scm_convention = SMC_CONVENTION_UNKNOWN;
static DEFINE_SPINLOCK(scm_query_lock);
@@ -444,10 +486,15 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
if (ret)
goto out;
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ return ret;
+
desc.args[1] = mdata_phys;
ret = qcom_scm_call(__scm->dev, &desc, &res);
+ qcom_scm_bw_disable();
qcom_scm_clk_disable();
out:
@@ -507,7 +554,12 @@ int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
if (ret)
return ret;
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ return ret;
+
ret = qcom_scm_call(__scm->dev, &desc, &res);
+ qcom_scm_bw_disable();
qcom_scm_clk_disable();
return ret ? : res.result[0];
@@ -537,7 +589,12 @@ int qcom_scm_pas_auth_and_reset(u32 peripheral)
if (ret)
return ret;
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ return ret;
+
ret = qcom_scm_call(__scm->dev, &desc, &res);
+ qcom_scm_bw_disable();
qcom_scm_clk_disable();
return ret ? : res.result[0];
@@ -566,8 +623,13 @@ int qcom_scm_pas_shutdown(u32 peripheral)
if (ret)
return ret;
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ return ret;
+
ret = qcom_scm_call(__scm->dev, &desc, &res);
+ qcom_scm_bw_disable();
qcom_scm_clk_disable();
return ret ? : res.result[0];
@@ -1277,8 +1339,15 @@ static int qcom_scm_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
+ mutex_init(&scm->scm_bw_lock);
+
clks = (unsigned long)of_device_get_match_data(&pdev->dev);
+ scm->path = devm_of_icc_get(&pdev->dev, NULL);
+ if (IS_ERR(scm->path))
+ return dev_err_probe(&pdev->dev, PTR_ERR(scm->path),
+ "failed to acquire interconnect path\n");
+
scm->core_clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(scm->core_clk)) {
if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)