diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h index a61646963020..db15f8cef11e 100644 --- a/drivers/remoteproc/qcom_common.h +++ b/drivers/remoteproc/qcom_common.h @@ -93,6 +93,7 @@ void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon); bool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon); uint32_t qcom_sysmon_get_txn_id(struct qcom_sysmon *sysmon); int qcom_sysmon_get_reason(struct qcom_sysmon *sysmon, char *buf, size_t len); +void qcom_sysmon_set_ops_stop(struct qcom_sysmon *sysmon, bool suspend); #else static inline struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, const char *name, @@ -120,6 +121,9 @@ static inline int qcom_sysmon_get_reason(struct qcom_sysmon *sysmon, { return -ENODEV; } + +static inline void qcom_sysmon_set_ops_stop(struct qcom_sysmon *sysmon, + bool suspend) { } #endif #endif diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 84bc8c4ac60f..9557d963e3c6 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "qcom_common.h" #include "qcom_pil_info.h" @@ -635,6 +636,54 @@ static int adsp_stop(struct rproc *rproc) return ret; } +static int adsp_shutdown(struct rproc *rproc) +{ + struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + int handover; + int ret; + + trace_rproc_qcom_event(dev_name(adsp->dev), "adsp_shutdown", "enter"); + + scm_pas_enable_bw(); + if (adsp->retry_shutdown) + ret = qcom_scm_pas_shutdown_retry(adsp->pas_id); + else + ret = qcom_scm_pas_shutdown(adsp->pas_id); + if (ret) + panic("Panicking, remoteproc %s failed to shutdown.\n", rproc->name); + + if (adsp->dtb_pas_id) { + ret = qcom_scm_pas_shutdown(adsp->dtb_pas_id); + if (ret) + panic("Panicking, remoteproc %s dtb failed to shutdown.\n", rproc->name); + } + + scm_pas_disable_bw(); + adsp_pds_disable(adsp, adsp->active_pds, adsp->active_pd_count); + if (adsp->qmp) + qcom_rproc_toggle_load_state(adsp->qmp, adsp->qmp_name, false); + handover = qcom_q6v5_unprepare(&adsp->q6v5); + if (handover) + qcom_pas_handover(&adsp->q6v5); + + trace_rproc_qcom_event(dev_name(adsp->dev), "adsp_shutdown", "exit"); + + return ret; +} + +void adsp_set_ops_stop(struct rproc *rproc, bool suspend) +{ + struct qcom_adsp *adsp; + + adsp = (struct qcom_adsp *)rproc->priv; + qcom_sysmon_set_ops_stop(adsp->sysmon, suspend); + if (suspend) + rproc->ops->stop = adsp_shutdown; + else + rproc->ops->stop = adsp_stop; +} +EXPORT_SYMBOL_GPL(adsp_set_ops_stop); + static int adsp_attach(struct rproc *rproc) { struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index 593a37b9210d..7e0c447eb26a 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -775,6 +775,51 @@ static void sysmon_stop(struct rproc_subdev *subdev, bool crashed) del_timer_sync(&sysmon->timeout_data.timer); } +static void sysmon_shutdown(struct rproc_subdev *subdev, bool crashed) +{ + unsigned long timeout; + struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev); + + trace_rproc_qcom_event(dev_name(sysmon->rproc->dev.parent), SYSMON_SUBDEV_NAME, + crashed ? "crash stop" : "stop"); + + sysmon->shutdown_acked = false; + + mutex_lock(&sysmon->state_lock); + sysmon->state = QCOM_SSR_BEFORE_SHUTDOWN; + + sysmon->transaction_id++; + dev_info(sysmon->dev, "Incrementing tid for %s to %d\n", sysmon->name, + sysmon->transaction_id); + + blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon); + mutex_unlock(&sysmon->state_lock); + + /* Don't request graceful shutdown if we've crashed */ + if (crashed) + return; + + sysmon->timeout_data.timer.function = sysmon_shutdown_notif_timeout_handler; + timeout = jiffies + msecs_to_jiffies(SYSMON_NOTIF_TIMEOUT); + mod_timer(&sysmon->timeout_data.timer, timeout); + + if (sysmon->ssctl_instance) { + if (!wait_for_completion_timeout(&sysmon->ssctl_comp, HZ / 2)) + dev_err(sysmon->dev, "timeout waiting for ssctl service\n"); + } + + del_timer_sync(&sysmon->timeout_data.timer); +} + +void qcom_sysmon_set_ops_stop(struct qcom_sysmon *sysmon, bool suspend) +{ + if (suspend) + sysmon->subdev.stop = sysmon_shutdown; + else + sysmon->subdev.stop = sysmon_stop; +} +EXPORT_SYMBOL_GPL(qcom_sysmon_set_ops_stop); + static void sysmon_unprepare(struct rproc_subdev *subdev) { struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, diff --git a/drivers/soc/qcom/power_state.c b/drivers/soc/qcom/power_state.c index b48f35c658fe..fd7fdfd4153e 100644 --- a/drivers/soc/qcom/power_state.c +++ b/drivers/soc/qcom/power_state.c @@ -124,6 +124,7 @@ static int subsys_suspend(struct subsystem_data *ss_data, struct rproc *rproc, u case SUBSYS_DEEPSLEEP: case SUBSYS_HIBERNATE: ss_data->ignore_ssr = true; + adsp_set_ops_stop(rproc, true); rproc_shutdown(rproc); ss_data->ignore_ssr = false; break; @@ -144,6 +145,7 @@ static int subsys_resume(struct subsystem_data *ss_data, struct rproc *rproc, u3 case SUBSYS_DEEPSLEEP: case SUBSYS_HIBERNATE: ss_data->ignore_ssr = true; + adsp_set_ops_stop(rproc, false); ret = rproc_boot(rproc); ss_data->ignore_ssr = false; break; diff --git a/include/linux/remoteproc/qcom_rproc.h b/include/linux/remoteproc/qcom_rproc.h index bb84ce48f58b..82c7e078cd42 100644 --- a/include/linux/remoteproc/qcom_rproc.h +++ b/include/linux/remoteproc/qcom_rproc.h @@ -61,6 +61,14 @@ static inline int qcom_rproc_set_dtb_firmware(struct rproc *rproc, const char *d { return -EINVAL; } + + +#endif + +#if IS_ENABLED(CONFIG_QCOM_Q6V5_PAS) +void adsp_set_ops_stop(struct rproc *rproc, bool suspend); +#else +static inline void adsp_set_ops_stop(struct rproc *rproc, bool suspend) { } #endif #endif