Like qcom_sysmon's SSCTL notify, clustered remote processors (e.g.
Nord's HPASS ADSP0/1/2) cannot handle the glink_ssr DO_CLEANUP
broadcast sent to siblings whenever one of them starts or stops,
leaving qcom_glink_ssr_notifier_call() to time out waiting for
CLEANUP_DONE.

Pass the stopping remoteproc's cluster identifier through
qcom_glink_ssr_notify(), and plumb each edge's own cluster identifier
down from the remoteproc that registers it, so that
qcom_glink_ssr_notifier_call() can skip the DO_CLEANUP send when the
sender and the receiving edge share a cluster. As in qcom_sysmon, the
suppression is done on the receiving side, so that a cluster member
keeps sending DO_CLEANUP to, and receiving it from, remote processors
outside its cluster. Edges with no cluster set are unaffected.

The receiving edge's identifier is carried in struct qcom_glink rather
than looked up from the rproc that owns it, so that glink_ssr does not
have to reach into struct remoteproc: RPMSG_QCOM_GLINK does not depend
on REMOTEPROC, and the identifier is deliberately opaque to keep the
two subsystems independent of each other.

Assisted-by: LLM
Signed-off-by: Shawn Guo <[email protected]>
---
 drivers/remoteproc/qcom_common.c  |  7 +++---
 drivers/remoteproc/qcom_common.h  |  2 +-
 drivers/rpmsg/qcom_glink_native.c | 20 ++++++++++++++++-
 drivers/rpmsg/qcom_glink_native.h |  5 ++++-
 drivers/rpmsg/qcom_glink_rpm.c    |  2 +-
 drivers/rpmsg/qcom_glink_smem.c   |  5 +++--
 drivers/rpmsg/qcom_glink_ssr.c    | 36 ++++++++++++++++++++++++++-----
 include/linux/rpmsg/qcom_glink.h  | 10 +++++----
 8 files changed, 69 insertions(+), 18 deletions(-)

diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c
index 5294e327f158..13458bda6b24 100644
--- a/drivers/remoteproc/qcom_common.c
+++ b/drivers/remoteproc/qcom_common.c
@@ -217,7 +217,8 @@ static int glink_subdev_start(struct rproc_subdev *subdev)
 {
        struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
 
-       glink->edge = qcom_glink_smem_register(glink->dev, glink->node);
+       glink->edge = qcom_glink_smem_register(&glink->rproc->dev, glink->node,
+                                              glink->rproc->cluster);
 
        return PTR_ERR_OR_ZERO(glink->edge);
 }
@@ -234,7 +235,7 @@ static void glink_subdev_unprepare(struct rproc_subdev 
*subdev)
 {
        struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
 
-       qcom_glink_ssr_notify(glink->ssr_name);
+       qcom_glink_ssr_notify(glink->ssr_name, glink->rproc->cluster);
 }
 
 /**
@@ -258,7 +259,7 @@ void qcom_add_glink_subdev(struct rproc *rproc, struct 
qcom_rproc_glink *glink,
                return;
        }
 
-       glink->dev = dev;
+       glink->rproc = rproc;
        glink->subdev.start = glink_subdev_start;
        glink->subdev.stop = glink_subdev_stop;
        glink->subdev.unprepare = glink_subdev_unprepare;
diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h
index b0e7e336d363..7136494cbfba 100644
--- a/drivers/remoteproc/qcom_common.h
+++ b/drivers/remoteproc/qcom_common.h
@@ -14,7 +14,7 @@ struct qcom_rproc_glink {
 
        const char *ssr_name;
 
-       struct device *dev;
+       struct rproc *rproc;
        struct device_node *node;
        struct qcom_glink_smem *edge;
 };
diff --git a/drivers/rpmsg/qcom_glink_native.c 
b/drivers/rpmsg/qcom_glink_native.c
index 55793fc18293..06494f4d3a4f 100644
--- a/drivers/rpmsg/qcom_glink_native.c
+++ b/drivers/rpmsg/qcom_glink_native.c
@@ -122,6 +122,8 @@ struct qcom_glink {
        unsigned long features;
 
        bool intentless;
+       /* Cluster identifier of the remote processor behind this edge, or NULL 
*/
+       void *cluster;
        wait_queue_head_t tx_avail_notify;
        bool sent_read_notify;
 
@@ -1889,7 +1891,8 @@ struct qcom_glink *qcom_glink_native_probe(struct device 
*dev,
                                           unsigned long features,
                                           struct qcom_glink_pipe *rx,
                                           struct qcom_glink_pipe *tx,
-                                          bool intentless)
+                                          bool intentless,
+                                          void *cluster)
 {
        int ret;
        struct qcom_glink *glink;
@@ -1904,6 +1907,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device 
*dev,
 
        glink->features = features;
        glink->intentless = intentless;
+       glink->cluster = cluster;
 
        spin_lock_init(&glink->tx_lock);
        spin_lock_init(&glink->rx_lock);
@@ -1937,6 +1941,20 @@ struct qcom_glink *qcom_glink_native_probe(struct device 
*dev,
 }
 EXPORT_SYMBOL_GPL(qcom_glink_native_probe);
 
+/**
+ * qcom_glink_ept_cluster() - cluster identifier of an endpoint's edge
+ * @ept:       endpoint to query
+ *
+ * Return: the opaque cluster identifier of the remote processor @ept talks to,
+ * or NULL if it is not part of a cluster.
+ */
+void *qcom_glink_ept_cluster(struct rpmsg_endpoint *ept)
+{
+       struct glink_channel *channel = to_glink_channel(ept);
+
+       return channel->glink->cluster;
+}
+
 static int qcom_glink_remove_device(struct device *dev, void *data)
 {
        device_unregister(dev);
diff --git a/drivers/rpmsg/qcom_glink_native.h 
b/drivers/rpmsg/qcom_glink_native.h
index 8dbec24de23e..8544f4f5c545 100644
--- a/drivers/rpmsg/qcom_glink_native.h
+++ b/drivers/rpmsg/qcom_glink_native.h
@@ -29,12 +29,15 @@ struct qcom_glink_pipe {
 
 struct device;
 struct qcom_glink;
+struct rpmsg_endpoint;
 
 struct qcom_glink *qcom_glink_native_probe(struct device *dev,
                                           unsigned long features,
                                           struct qcom_glink_pipe *rx,
                                           struct qcom_glink_pipe *tx,
-                                          bool intentless);
+                                          bool intentless,
+                                          void *cluster);
+void *qcom_glink_ept_cluster(struct rpmsg_endpoint *ept);
 void qcom_glink_native_remove(struct qcom_glink *glink);
 void qcom_glink_native_rx(struct qcom_glink *glink);
 
diff --git a/drivers/rpmsg/qcom_glink_rpm.c b/drivers/rpmsg/qcom_glink_rpm.c
index e3ba2c63a5fc..9c1ff05d4a10 100644
--- a/drivers/rpmsg/qcom_glink_rpm.c
+++ b/drivers/rpmsg/qcom_glink_rpm.c
@@ -346,7 +346,7 @@ static int glink_rpm_probe(struct platform_device *pdev)
                                        0,
                                        &rpm->rx_pipe.native,
                                        &rpm->tx_pipe.native,
-                                       true);
+                                       true, NULL);
        if (IS_ERR(glink)) {
                mbox_free_channel(rpm->mbox_chan);
                return PTR_ERR(glink);
diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c
index 35bb03e67ae8..938f9575784e 100644
--- a/drivers/rpmsg/qcom_glink_smem.c
+++ b/drivers/rpmsg/qcom_glink_smem.c
@@ -225,7 +225,8 @@ static void qcom_glink_smem_release(struct device *dev)
 }
 
 struct qcom_glink_smem *qcom_glink_smem_register(struct device *parent,
-                                                struct device_node *node)
+                                                struct device_node *node,
+                                                void *cluster)
 {
        struct glink_smem_pipe *rx_pipe;
        struct glink_smem_pipe *tx_pipe;
@@ -345,7 +346,7 @@ struct qcom_glink_smem *qcom_glink_smem_register(struct 
device *parent,
        glink = qcom_glink_native_probe(dev,
                                        GLINK_FEATURE_INTENT_REUSE,
                                        &rx_pipe->native, &tx_pipe->native,
-                                       false);
+                                       false, cluster);
        if (IS_ERR(glink)) {
                ret = PTR_ERR(glink);
                goto err_free_mbox;
diff --git a/drivers/rpmsg/qcom_glink_ssr.c b/drivers/rpmsg/qcom_glink_ssr.c
index e71d3716c55c..74c671a19af0 100644
--- a/drivers/rpmsg/qcom_glink_ssr.c
+++ b/drivers/rpmsg/qcom_glink_ssr.c
@@ -11,6 +11,8 @@
 #include <linux/rpmsg/qcom_glink.h>
 #include <linux/remoteproc/qcom_rproc.h>
 
+#include "qcom_glink_native.h"
+
 /**
  * struct do_cleanup_msg - The data structure for an SSR do_cleanup message
  * @version:   The G-Link SSR protocol version
@@ -55,16 +57,34 @@ struct glink_ssr {
        struct completion completion;
 };
 
+/**
+ * struct glink_ssr_notify_data - payload of an SSR notification
+ * @ssr_name:  name of the remoteproc that has been stopped
+ * @cluster:   cluster identifier of the remoteproc that has been stopped,
+ *             or NULL if it is not part of a cluster
+ */
+struct glink_ssr_notify_data {
+       const char *ssr_name;
+       void *cluster;
+};
+
 /* Notifier list for all registered glink_ssr instances */
 static BLOCKING_NOTIFIER_HEAD(ssr_notifiers);
 
 /**
  * qcom_glink_ssr_notify() - notify GLINK SSR about stopped remoteproc
  * @ssr_name:  name of the remoteproc that has been stopped
+ * @cluster:   cluster identifier of the remoteproc that has been stopped,
+ *             or NULL if it is not part of a cluster
  */
-void qcom_glink_ssr_notify(const char *ssr_name)
+void qcom_glink_ssr_notify(const char *ssr_name, void *cluster)
 {
-       blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr_name);
+       struct glink_ssr_notify_data data = {
+               .ssr_name = ssr_name,
+               .cluster = cluster,
+       };
+
+       blocking_notifier_call_chain(&ssr_notifiers, 0, &data);
 }
 EXPORT_SYMBOL_GPL(qcom_glink_ssr_notify);
 
@@ -100,18 +120,24 @@ static int qcom_glink_ssr_notifier_call(struct 
notifier_block *nb,
                                        void *data)
 {
        struct glink_ssr *ssr = container_of(nb, struct glink_ssr, nb);
+       struct glink_ssr_notify_data *notify_data = data;
        struct do_cleanup_msg msg;
-       char *ssr_name = data;
+       void *cluster;
        int ret;
 
+       /* Cluster siblings' firmware can't handle peer SSR notify; skip it */
+       cluster = qcom_glink_ept_cluster(ssr->ept);
+       if (cluster && cluster == notify_data->cluster)
+               return NOTIFY_DONE;
+
        ssr->seq_num++;
        reinit_completion(&ssr->completion);
 
        memset(&msg, 0, sizeof(msg));
        msg.command = cpu_to_le32(GLINK_SSR_DO_CLEANUP);
        msg.seq_num = cpu_to_le32(ssr->seq_num);
-       msg.name_len = cpu_to_le32(strlen(ssr_name));
-       strscpy(msg.name, ssr_name, sizeof(msg.name));
+       msg.name_len = cpu_to_le32(strlen(notify_data->ssr_name));
+       strscpy(msg.name, notify_data->ssr_name, sizeof(msg.name));
 
        ret = rpmsg_send(ssr->ept, &msg, sizeof(msg));
        if (ret < 0)
diff --git a/include/linux/rpmsg/qcom_glink.h b/include/linux/rpmsg/qcom_glink.h
index bfbd48f435fa..6591d016e8cb 100644
--- a/include/linux/rpmsg/qcom_glink.h
+++ b/include/linux/rpmsg/qcom_glink.h
@@ -8,22 +8,24 @@
 struct qcom_glink_smem;
 
 #if IS_ENABLED(CONFIG_RPMSG_QCOM_GLINK)
-void qcom_glink_ssr_notify(const char *ssr_name);
+void qcom_glink_ssr_notify(const char *ssr_name, void *cluster);
 #else
-static inline void qcom_glink_ssr_notify(const char *ssr_name) {}
+static inline void qcom_glink_ssr_notify(const char *ssr_name, void *cluster) 
{}
 #endif
 
 #if IS_ENABLED(CONFIG_RPMSG_QCOM_GLINK_SMEM)
 
 struct qcom_glink_smem *qcom_glink_smem_register(struct device *parent,
-                                                struct device_node *node);
+                                                struct device_node *node,
+                                                void *cluster);
 void qcom_glink_smem_unregister(struct qcom_glink_smem *glink);
 
 #else
 
 static inline struct qcom_glink_smem *
 qcom_glink_smem_register(struct device *parent,
-                        struct device_node *node)
+                        struct device_node *node,
+                        void *cluster)
 {
        return NULL;
 }
-- 
2.43.0


Reply via email to