summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArve Hjønnevåg <arve@android.com>2014-10-04 17:36:20 -0700
committerArve Hjønnevåg <arve@android.com>2014-10-06 22:10:26 +0000
commit875fe0220bba1cc6aa4c9dc6f56b43a5e1a13680 (patch)
treed7c5392578abb05af50b362df139e53fbaa37629
parentc03aef7a7c762d8bc8ec5430c5cf26622c86afad (diff)
downloadtegra-875fe0220bba1cc6aa4c9dc6f56b43a5e1a13680.tar.gz
trusty: Retry std_calls on SM_ERR_BUSY
If the trusty spinlock is held, or if the strex fails for another reason, trusty returns SM_ERR_BUSY. Add retry code to handle this. Without this retry code, std_calls can fail. If the previous smc call had returned SM_ERR_INTERRUPTED, this failure would cause the driver to get out of sync with trusty. All later calls would then fail with SM_ERR_INTERLEAVED_SMC. Change-Id: I9f72bb7ce9af9e1ef0a38aeff62fb83ea51377cf Signed-off-by: Arve Hjønnevåg <arve@android.com> (cherry picked from commit 84925a939f207ccbc0a2536a20f49de5040bb907)
-rw-r--r--drivers/trusty/trusty.c78
1 files changed, 64 insertions, 14 deletions
diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c
index c3c9c8fc1b4d..209e7c209e07 100644
--- a/drivers/trusty/trusty.c
+++ b/drivers/trusty/trusty.c
@@ -13,6 +13,7 @@
*/
#include <asm/compiler.h>
+#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
@@ -93,6 +94,67 @@ s64 trusty_fast_call64(struct device *dev, u64 smcnr, u64 a0, u64 a1, u64 a2)
}
#endif
+static ulong trusty_std_call_inner(struct device *dev, ulong smcnr,
+ ulong a0, ulong a1, ulong a2)
+{
+ ulong ret;
+ int retry = 5;
+
+ dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx)\n",
+ __func__, smcnr, a0, a1, a2);
+ while (true) {
+ ret = smc(smcnr, a0, a1, a2);
+ if ((int)ret != SM_ERR_BUSY || !retry)
+ break;
+
+ dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, retry\n",
+ __func__, smcnr, a0, a1, a2);
+ retry--;
+ }
+
+ return ret;
+}
+
+static ulong trusty_std_call_helper(struct device *dev, ulong smcnr,
+ ulong a0, ulong a1, ulong a2)
+{
+ ulong ret;
+ int sleep_time = 1;
+ struct trusty_state *s = platform_get_drvdata(to_platform_device(dev));
+
+ while (true) {
+ local_irq_disable();
+ atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE,
+ NULL);
+ ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2);
+ atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED,
+ NULL);
+ local_irq_enable();
+
+ if ((int)ret != SM_ERR_BUSY)
+ break;
+
+ if (sleep_time == 256)
+ dev_warn(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy\n",
+ __func__, smcnr, a0, a1, a2);
+ dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, wait %d ms\n",
+ __func__, smcnr, a0, a1, a2, sleep_time);
+
+ msleep(sleep_time);
+ if (sleep_time < 1000)
+ sleep_time <<= 1;
+
+ dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) retry\n",
+ __func__, smcnr, a0, a1, a2);
+ }
+
+ if (sleep_time > 256)
+ dev_warn(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) busy cleared\n",
+ __func__, smcnr, a0, a1, a2);
+
+ return ret;
+}
+
s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2)
{
int ret;
@@ -103,30 +165,18 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2)
mutex_lock(&s->smc_lock);
- local_irq_disable();
dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) started\n",
__func__, smcnr, a0, a1, a2);
- atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE, NULL);
- ret = smc(smcnr, a0, a1, a2);
- atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED, NULL);
-
+ ret = trusty_std_call_helper(dev, smcnr, a0, a1, a2);
while (ret == SM_ERR_INTERRUPTED) {
dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) interrupted\n",
__func__, smcnr, a0, a1, a2);
- local_irq_enable();
- local_irq_disable();
- atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE,
- NULL);
- ret = smc(SMC_SC_RESTART_LAST, 0, 0, 0);
- atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED,
- NULL);
+ ret = trusty_std_call_helper(dev, SMC_SC_RESTART_LAST, 0, 0, 0);
}
dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) returned 0x%x\n",
__func__, smcnr, a0, a1, a2, ret);
- local_irq_enable();
-
mutex_unlock(&s->smc_lock);
return ret;