[PATCH v2 2/4] remoteproc: qcom: q6v5: Add shutdown-ack irq

From: Sibi Sankar
Date: Mon Dec 24 2018 - 03:48:52 EST


Add shutdown-ack irq handling for Q6V5. This patch includes enabling
shutdown-ack irq on those Q6V5 instances with "has_shutdown_irq"
flag set and exposing Q6V5 state information to the sysmon instance
which is required to ascertain graceful shutdown completion.

Signed-off-by: Sibi Sankar <sibis@xxxxxxxxxxxxxx>
---

v2:
Move shutdown-irq get to Q6V5 from sysmon to handle
-EPROBE_DEFER cases
Correct the shutdown-irq wait time to 10 * HZ

drivers/remoteproc/qcom_common.h | 7 ++--
drivers/remoteproc/qcom_q6v5.c | 53 +++++++++++++++++++++++++++++
drivers/remoteproc/qcom_q6v5.h | 5 +++
drivers/remoteproc/qcom_q6v5_adsp.c | 3 +-
drivers/remoteproc/qcom_q6v5_mss.c | 3 +-
drivers/remoteproc/qcom_q6v5_pas.c | 3 +-
drivers/remoteproc/qcom_sysmon.c | 6 +++-
drivers/remoteproc/qcom_wcnss.c | 3 +-
8 files changed, 76 insertions(+), 7 deletions(-)

diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h
index 58de71e4781c..d938b09ad02c 100644
--- a/drivers/remoteproc/qcom_common.h
+++ b/drivers/remoteproc/qcom_common.h
@@ -7,6 +7,7 @@
#include <linux/soc/qcom/qmi.h>

struct qcom_sysmon;
+struct qcom_q6v5;

struct qcom_rproc_glink {
struct rproc_subdev subdev;
@@ -45,12 +46,14 @@ void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr);
#if IS_ENABLED(CONFIG_QCOM_SYSMON)
struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
const char *name,
- int ssctl_instance);
+ int ssctl_instance,
+ struct qcom_q6v5 *q6v5);
void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon);
#else
static inline struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
const char *name,
- int ssctl_instance)
+ int ssctl_instance,
+ struct qcom_q6v5 *q6v5)
{
return NULL;
}
diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c
index 0d33e3079f0d..a4c2ecae6a0f 100644
--- a/drivers/remoteproc/qcom_q6v5.c
+++ b/drivers/remoteproc/qcom_q6v5.c
@@ -25,6 +25,7 @@ int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5)
{
reinit_completion(&q6v5->start_done);
reinit_completion(&q6v5->stop_done);
+ reinit_completion(&q6v5->shutdown_done);

q6v5->running = true;
q6v5->handover_issued = false;
@@ -141,6 +142,35 @@ static irqreturn_t q6v5_stop_interrupt(int irq, void *data)
return IRQ_HANDLED;
}

+static irqreturn_t q6v5_shutdown_interrupt(int irq, void *data)
+{
+ struct qcom_q6v5 *q6v5 = data;
+
+ complete(&q6v5->shutdown_done);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * qcom_q6v5_wait_for_shutdown() - wait for remote processor shutdown signal
+ * @q6v5: reference to qcom_q6v5 context
+ * @timeout: timeout to wait for the event, in jiffies
+ *
+ * Return: 0 on success, -ETIMEDOUT on timeout
+ */
+int qcom_q6v5_wait_for_shutdown(struct qcom_q6v5 *q6v5, int timeout)
+{
+ int ret;
+
+ if (!q6v5->has_shutdown_irq)
+ return 0;
+
+ ret = wait_for_completion_timeout(&q6v5->shutdown_done, timeout);
+
+ return !ret ? -ETIMEDOUT : 0;
+}
+EXPORT_SYMBOL_GPL(qcom_q6v5_wait_for_shutdown);
+
/**
* qcom_q6v5_request_stop() - request the remote processor to stop
* @q6v5: reference to qcom_q6v5 context
@@ -185,6 +215,7 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev,

init_completion(&q6v5->start_done);
init_completion(&q6v5->stop_done);
+ init_completion(&q6v5->shutdown_done);

q6v5->wdog_irq = platform_get_irq_byname(pdev, "wdog");
if (q6v5->wdog_irq < 0) {
@@ -277,6 +308,28 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev,
return ret;
}

+ if (q6v5->has_shutdown_irq) {
+ q6v5->shutdown_irq = platform_get_irq_byname(pdev,
+ "shutdown-ack");
+ if (q6v5->shutdown_irq < 0) {
+ if (q6v5->shutdown_irq != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "failed to get shutdown-ack IRQ: %d\n",
+ q6v5->shutdown_irq);
+ return q6v5->shutdown_irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, q6v5->shutdown_irq,
+ NULL, q6v5_shutdown_interrupt,
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT,
+ "q6v5 shutdown", q6v5);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to acquire shutdown IRQ\n");
+ return ret;
+ }
+ }
+
q6v5->state = qcom_smem_state_get(&pdev->dev, "stop", &q6v5->stop_bit);
if (IS_ERR(q6v5->state)) {
dev_err(&pdev->dev, "failed to acquire stop state\n");
diff --git a/drivers/remoteproc/qcom_q6v5.h b/drivers/remoteproc/qcom_q6v5.h
index 7ac92c1e0f49..5cbaf2564c3a 100644
--- a/drivers/remoteproc/qcom_q6v5.h
+++ b/drivers/remoteproc/qcom_q6v5.h
@@ -21,11 +21,15 @@ struct qcom_q6v5 {
int ready_irq;
int handover_irq;
int stop_irq;
+ int shutdown_irq;
+
+ u8 has_shutdown_irq;

bool handover_issued;

struct completion start_done;
struct completion stop_done;
+ struct completion shutdown_done;

int crash_reason;

@@ -42,5 +46,6 @@ int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5);
int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5);
int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5);
int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout);
+int qcom_q6v5_wait_for_shutdown(struct qcom_q6v5 *q6v5, int timeout);

#endif
diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c
index 79374d1de311..5fc42d38a1cd 100644
--- a/drivers/remoteproc/qcom_q6v5_adsp.c
+++ b/drivers/remoteproc/qcom_q6v5_adsp.c
@@ -438,7 +438,8 @@ static int adsp_probe(struct platform_device *pdev)
qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
adsp->sysmon = qcom_add_sysmon_subdev(rproc,
desc->sysmon_name,
- desc->ssctl_id);
+ desc->ssctl_id,
+ &adsp->q6v5);

ret = rproc_add(rproc);
if (ret)
diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c
index 01be7314e176..3bc2dec85928 100644
--- a/drivers/remoteproc/qcom_q6v5_mss.c
+++ b/drivers/remoteproc/qcom_q6v5_mss.c
@@ -1340,7 +1340,8 @@ static int q6v5_probe(struct platform_device *pdev)
qcom_add_glink_subdev(rproc, &qproc->glink_subdev);
qcom_add_smd_subdev(rproc, &qproc->smd_subdev);
qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss");
- qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12);
+ qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12,
+ &qproc->q6v5);

ret = rproc_add(rproc);
if (ret)
diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
index b1e63fcd5fdf..920a39ea6609 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -303,7 +303,8 @@ static int adsp_probe(struct platform_device *pdev)
qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
adsp->sysmon = qcom_add_sysmon_subdev(rproc,
desc->sysmon_name,
- desc->ssctl_id);
+ desc->ssctl_id,
+ &adsp->q6v5);

ret = rproc_add(rproc);
if (ret)
diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c
index e976a602b015..c0d6ee8de995 100644
--- a/drivers/remoteproc/qcom_sysmon.c
+++ b/drivers/remoteproc/qcom_sysmon.c
@@ -14,12 +14,14 @@
#include <linux/rpmsg.h>

#include "qcom_common.h"
+#include "qcom_q6v5.h"

static BLOCKING_NOTIFIER_HEAD(sysmon_notifiers);

struct qcom_sysmon {
struct rproc_subdev subdev;
struct rproc *rproc;
+ struct qcom_q6v5 *q6v5;

struct list_head node;

@@ -442,7 +444,8 @@ static int sysmon_notify(struct notifier_block *nb, unsigned long event,
*/
struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
const char *name,
- int ssctl_instance)
+ int ssctl_instance,
+ struct qcom_q6v5 *q6v5)
{
struct qcom_sysmon *sysmon;
int ret;
@@ -456,6 +459,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,

sysmon->name = name;
sysmon->ssctl_instance = ssctl_instance;
+ sysmon->q6v5 = q6v5;

init_completion(&sysmon->comp);
mutex_init(&sysmon->lock);
diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c
index b0e07e9f42d5..af13cade35da 100644
--- a/drivers/remoteproc/qcom_wcnss.c
+++ b/drivers/remoteproc/qcom_wcnss.c
@@ -552,7 +552,8 @@ static int wcnss_probe(struct platform_device *pdev)
}

qcom_add_smd_subdev(rproc, &wcnss->smd_subdev);
- wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID);
+ wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID,
+ NULL);

ret = rproc_add(rproc);
if (ret)
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project