summaryrefslogtreecommitdiff
path: root/drivers/firmware/qcom_scm.c
diff options
context:
space:
mode:
authorStephen Boyd <swboyd@chromium.org>2021-02-23 13:45:35 -0800
committerBjorn Andersson <bjorn.andersson@linaro.org>2021-04-06 21:25:23 -0500
commitf6ea568f0ddcdfad52807110ed8983e610f0e03b (patch)
tree7dfa9dccdbb2e586536b293d97d2ff35502e9591 /drivers/firmware/qcom_scm.c
parent9d11af8b06a811c5c4878625f51ce109e2af4e80 (diff)
firmware: qcom_scm: Reduce locking section for __get_convention()
We shouldn't need to hold this spinlock here around the entire SCM call into the firmware and back. Instead, we should be able to query the firmware, potentially in parallel with other CPUs making the same convention detection firmware call, and then grab the lock to update the calling convention detected. The convention doesn't change at runtime so calling into firmware more than once is possibly wasteful but simpler. Besides, this is the slow path, not the fast path where we've already detected the convention used. More importantly, this allows us to add more logic here to workaround the case where the firmware call to check for availability isn't implemented in the firmware at all. In that case we can check the firmware node compatible string and force a calling convention. Note that we remove the 'has_queried' logic that is repeated twice. That could lead to the calling convention being printed multiple times to the kernel logs if the bool is true but __query_convention() is running on multiple CPUs. We also shorten the time where the lock is held, but we keep the lock held around the printk because it doesn't seem hugely important to drop it for that. Cc: Elliot Berman <eberman@codeaurora.org> Cc: Brian Masney <masneyb@onstation.org> Cc: Stephan Gerhold <stephan@gerhold.net> Cc: Jeffrey Hugo <jhugo@codeaurora.org> Cc: Douglas Anderson <dianders@chromium.org> Fixes: 9a434cee773a ("firmware: qcom_scm: Dynamically support SMCCC and legacy conventions") Signed-off-by: Stephen Boyd <swboyd@chromium.org> Link: https://lore.kernel.org/r/20210223214539.1336155-3-swboyd@chromium.org Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Diffstat (limited to 'drivers/firmware/qcom_scm.c')
-rw-r--r--drivers/firmware/qcom_scm.c55
1 files changed, 27 insertions, 28 deletions
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index 2be5573dce53..21e07a464bd9 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -113,11 +113,10 @@ static void qcom_scm_clk_disable(void)
clk_disable_unprepare(__scm->bus_clk);
}
-enum qcom_scm_convention qcom_scm_convention;
-static bool has_queried __read_mostly;
-static DEFINE_SPINLOCK(query_lock);
+enum qcom_scm_convention qcom_scm_convention = SMC_CONVENTION_UNKNOWN;
+static DEFINE_SPINLOCK(scm_query_lock);
-static void __query_convention(void)
+static enum qcom_scm_convention __get_convention(void)
{
unsigned long flags;
struct qcom_scm_desc desc = {
@@ -130,36 +129,36 @@ static void __query_convention(void)
.owner = ARM_SMCCC_OWNER_SIP,
};
struct qcom_scm_res res;
+ enum qcom_scm_convention probed_convention;
int ret;
- spin_lock_irqsave(&query_lock, flags);
- if (has_queried)
- goto out;
+ if (likely(qcom_scm_convention != SMC_CONVENTION_UNKNOWN))
+ return qcom_scm_convention;
- qcom_scm_convention = SMC_CONVENTION_ARM_64;
- // Device isn't required as there is only one argument - no device
- // needed to dma_map_single to secure world
- ret = scm_smc_call(NULL, &desc, &res, true);
+ /*
+ * Device isn't required as there is only one argument - no device
+ * needed to dma_map_single to secure world
+ */
+ probed_convention = SMC_CONVENTION_ARM_64;
+ ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true);
if (!ret && res.result[0] == 1)
- goto out;
+ goto found;
- qcom_scm_convention = SMC_CONVENTION_ARM_32;
- ret = scm_smc_call(NULL, &desc, &res, true);
+ probed_convention = SMC_CONVENTION_ARM_32;
+ ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true);
if (!ret && res.result[0] == 1)
- goto out;
-
- qcom_scm_convention = SMC_CONVENTION_LEGACY;
-out:
- has_queried = true;
- spin_unlock_irqrestore(&query_lock, flags);
- pr_info("qcom_scm: convention: %s\n",
- qcom_scm_convention_names[qcom_scm_convention]);
-}
+ goto found;
+
+ probed_convention = SMC_CONVENTION_LEGACY;
+found:
+ spin_lock_irqsave(&scm_query_lock, flags);
+ if (probed_convention != qcom_scm_convention) {
+ qcom_scm_convention = probed_convention;
+ pr_info("qcom_scm: convention: %s\n",
+ qcom_scm_convention_names[qcom_scm_convention]);
+ }
+ spin_unlock_irqrestore(&scm_query_lock, flags);
-static inline enum qcom_scm_convention __get_convention(void)
-{
- if (unlikely(!has_queried))
- __query_convention();
return qcom_scm_convention;
}
@@ -1239,7 +1238,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
__scm = scm;
__scm->dev = &pdev->dev;
- __query_convention();
+ __get_convention();
/*
* If requested enable "download mode", from this point on warmboot