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