remoteproc: sysmon: Expose the shutdown result
[linux-2.6-microblaze.git] / drivers / remoteproc / qcom_sysmon.c
index 13284d9..c7af700 100644 (file)
@@ -44,6 +44,7 @@ struct qcom_sysmon {
        struct mutex lock;
 
        bool ssr_ack;
+       bool shutdown_acked;
 
        struct qmi_handle qmi;
        struct sockaddr_qrtr ssctl;
@@ -115,10 +116,13 @@ out_unlock:
 /**
  * sysmon_request_shutdown() - request graceful shutdown of remote
  * @sysmon:    sysmon context
+ *
+ * Return: boolean indicator of the remote processor acking the request
  */
-static void sysmon_request_shutdown(struct qcom_sysmon *sysmon)
+static bool sysmon_request_shutdown(struct qcom_sysmon *sysmon)
 {
        char *req = "ssr:shutdown";
+       bool acked = false;
        int ret;
 
        mutex_lock(&sysmon->lock);
@@ -141,9 +145,13 @@ static void sysmon_request_shutdown(struct qcom_sysmon *sysmon)
        if (!sysmon->ssr_ack)
                dev_err(sysmon->dev,
                        "unexpected response to sysmon shutdown request\n");
+       else
+               acked = true;
 
 out_unlock:
        mutex_unlock(&sysmon->lock);
+
+       return acked;
 }
 
 static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count,
@@ -297,14 +305,33 @@ static const struct qmi_msg_handler qmi_indication_handler[] = {
        {}
 };
 
+static bool ssctl_request_shutdown_wait(struct qcom_sysmon *sysmon)
+{
+       int ret;
+
+       ret = wait_for_completion_timeout(&sysmon->shutdown_comp, 10 * HZ);
+       if (ret)
+               return true;
+
+       ret = try_wait_for_completion(&sysmon->ind_comp);
+       if (ret)
+               return true;
+
+       dev_err(sysmon->dev, "timeout waiting for shutdown ack\n");
+       return false;
+}
+
 /**
  * ssctl_request_shutdown() - request shutdown via SSCTL QMI service
  * @sysmon:    sysmon context
+ *
+ * Return: boolean indicator of the remote processor acking the request
  */
-static void ssctl_request_shutdown(struct qcom_sysmon *sysmon)
+static bool ssctl_request_shutdown(struct qcom_sysmon *sysmon)
 {
        struct ssctl_shutdown_resp resp;
        struct qmi_txn txn;
+       bool acked = false;
        int ret;
 
        reinit_completion(&sysmon->ind_comp);
@@ -312,7 +339,7 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon)
        ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp);
        if (ret < 0) {
                dev_err(sysmon->dev, "failed to allocate QMI txn\n");
-               return;
+               return false;
        }
 
        ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
@@ -320,27 +347,23 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon)
        if (ret < 0) {
                dev_err(sysmon->dev, "failed to send shutdown request\n");
                qmi_txn_cancel(&txn);
-               return;
+               return false;
        }
 
        ret = qmi_txn_wait(&txn, 5 * HZ);
-       if (ret < 0)
+       if (ret < 0) {
                dev_err(sysmon->dev, "failed receiving QMI response\n");
-       else if (resp.resp.result)
+       } else if (resp.resp.result) {
                dev_err(sysmon->dev, "shutdown request failed\n");
-       else
+       } else {
                dev_dbg(sysmon->dev, "shutdown request completed\n");
-
-       if (sysmon->shutdown_irq > 0) {
-               ret = wait_for_completion_timeout(&sysmon->shutdown_comp,
-                                                 10 * HZ);
-               if (!ret) {
-                       ret = try_wait_for_completion(&sysmon->ind_comp);
-                       if (!ret)
-                               dev_err(sysmon->dev,
-                                       "timeout waiting for shutdown ack\n");
-               }
+               acked = true;
        }
+
+       if (sysmon->shutdown_irq > 0)
+               return ssctl_request_shutdown_wait(sysmon);
+
+       return acked;
 }
 
 /**
@@ -510,6 +533,9 @@ static void sysmon_stop(struct rproc_subdev *subdev, bool crashed)
                .subsys_name = sysmon->name,
                .ssr_event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN
        };
+       bool acked;
+
+       sysmon->shutdown_acked = false;
 
        mutex_lock(&sysmon->state_lock);
        sysmon->state = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN;
@@ -521,9 +547,11 @@ static void sysmon_stop(struct rproc_subdev *subdev, bool crashed)
                return;
 
        if (sysmon->ssctl_version)
-               ssctl_request_shutdown(sysmon);
+               acked = ssctl_request_shutdown(sysmon);
        else if (sysmon->ept)
-               sysmon_request_shutdown(sysmon);
+               acked = sysmon_request_shutdown(sysmon);
+
+       sysmon->shutdown_acked = acked;
 }
 
 static void sysmon_unprepare(struct rproc_subdev *subdev)
@@ -681,6 +709,22 @@ void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon)
 }
 EXPORT_SYMBOL_GPL(qcom_remove_sysmon_subdev);
 
+/**
+ * qcom_sysmon_shutdown_acked() - query the success of the last shutdown
+ * @sysmon:    sysmon context
+ *
+ * When sysmon is used to request a graceful shutdown of the remote processor
+ * this can be used by the remoteproc driver to query the success, in order to
+ * know if it should fall back to other means of requesting a shutdown.
+ *
+ * Return: boolean indicator of the success of the last shutdown request
+ */
+bool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon)
+{
+       return sysmon && sysmon->shutdown_acked;
+}
+EXPORT_SYMBOL_GPL(qcom_sysmon_shutdown_acked);
+
 /**
  * sysmon_probe() - probe sys_mon channel
  * @rpdev:     rpmsg device handle