summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/remoteproc/qcom_q6v5.c54
-rw-r--r--drivers/remoteproc/qcom_q6v5.h4
2 files changed, 48 insertions, 10 deletions
diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c
index a11d8ace554b..0206a4a19254 100644
--- a/drivers/remoteproc/qcom_q6v5.c
+++ b/drivers/remoteproc/qcom_q6v5.c
@@ -36,6 +36,40 @@ static int q6v5_load_state_toggle(struct qcom_q6v5 *q6v5, bool enable)
return ret;
}
+static void q6v5_handover_irq_enable(struct qcom_q6v5 *q6v5)
+{
+ unsigned long flags;
+ bool enable = false;
+
+ spin_lock_irqsave(&q6v5->handover_lock, flags);
+ if (!q6v5->handover_irq_enabled) {
+ q6v5->handover_irq_enabled = true;
+ enable = true;
+ }
+ spin_unlock_irqrestore(&q6v5->handover_lock, flags);
+
+ if (enable)
+ enable_irq(q6v5->handover_irq);
+}
+
+static void q6v5_handover_irq_disable(struct qcom_q6v5 *q6v5, bool sync)
+{
+ unsigned long flags;
+ bool disable = false;
+
+ spin_lock_irqsave(&q6v5->handover_lock, flags);
+ if (q6v5->handover_irq_enabled) {
+ q6v5->handover_irq_enabled = false;
+ disable = true;
+ }
+ spin_unlock_irqrestore(&q6v5->handover_lock, flags);
+
+ if (disable)
+ disable_irq_nosync(q6v5->handover_irq);
+ if (sync)
+ synchronize_irq(q6v5->handover_irq);
+}
+
/**
* qcom_q6v5_prepare() - reinitialize the qcom_q6v5 context before start
* @q6v5: reference to qcom_q6v5 context to be reinitialized
@@ -64,7 +98,7 @@ int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5)
q6v5->running = true;
q6v5->handover_issued = false;
- enable_irq(q6v5->handover_irq);
+ q6v5_handover_irq_enable(q6v5);
return 0;
}
@@ -78,7 +112,8 @@ EXPORT_SYMBOL_GPL(qcom_q6v5_prepare);
*/
int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5)
{
- disable_irq(q6v5->handover_irq);
+ q6v5_handover_irq_disable(q6v5, true);
+
q6v5_load_state_toggle(q6v5, false);
/* Disable interconnect vote, in case handover never happened */
@@ -164,18 +199,15 @@ static irqreturn_t q6v5_handover_interrupt(int irq, void *data)
{
struct qcom_q6v5 *q6v5 = data;
- if (q6v5->handover_issued) {
- dev_err(q6v5->dev, "Handover signaled, but it already happened\n");
- return IRQ_HANDLED;
- }
+ q6v5->handover_issued = true;
+
+ q6v5_handover_irq_disable(q6v5, false);
if (q6v5->handover)
q6v5->handover(q6v5);
icc_set_bw(q6v5->path, 0, 0);
- q6v5->handover_issued = true;
-
return IRQ_HANDLED;
}
@@ -257,6 +289,8 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev,
q6v5->crash_reason = crash_reason;
q6v5->handover = handover;
+ spin_lock_init(&q6v5->handover_lock);
+
init_completion(&q6v5->start_done);
init_completion(&q6v5->stop_done);
@@ -305,13 +339,13 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev,
ret = devm_request_threaded_irq(&pdev->dev, q6v5->handover_irq,
NULL, q6v5_handover_interrupt,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT |
+ IRQF_NO_AUTOEN,
"q6v5 handover", q6v5);
if (ret) {
dev_err(&pdev->dev, "failed to acquire handover IRQ\n");
return ret;
}
- disable_irq(q6v5->handover_irq);
q6v5->stop_irq = platform_get_irq_byname(pdev, "stop-ack");
if (q6v5->stop_irq < 0)
diff --git a/drivers/remoteproc/qcom_q6v5.h b/drivers/remoteproc/qcom_q6v5.h
index 5a859c41896e..8991ff090579 100644
--- a/drivers/remoteproc/qcom_q6v5.h
+++ b/drivers/remoteproc/qcom_q6v5.h
@@ -5,6 +5,7 @@
#include <linux/kernel.h>
#include <linux/completion.h>
+#include <linux/spinlock.h>
#include <linux/soc/qcom/qcom_aoss.h>
struct icc_path;
@@ -29,6 +30,9 @@ struct qcom_q6v5 {
int handover_irq;
int stop_irq;
+ /* Protects handover_irq_enabled against stop/handover races. */
+ spinlock_t handover_lock;
+ bool handover_irq_enabled;
bool handover_issued;
struct completion start_done;