diff mbox series

[RFC,06/18] remoteproc: Add component in core for child devices synchronization

Message ID 20200416161331.7606-7-arnaud.pouliquen@st.com (mailing list archive)
State New, archived
Headers show
Series remoteproc: Decorelate virtio from core | expand

Commit Message

Arnaud POULIQUEN April 16, 2020, 4:13 p.m. UTC
As virtio is now a platform device. We need to ensure that the virtio
device is initialized before subdev ops are called.
Add a rproc master component to synchronize child devices bind/unbind
before the resource allocation and the firmware start.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/remoteproc/remoteproc_core.c | 96 +++++++++++++++++++++++++++-
 include/linux/remoteproc.h           |  4 ++
 2 files changed, 98 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 5dcef62d8d1d..cb40aae12b98 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -16,6 +16,7 @@ 
 
 #define pr_fmt(fmt)    "%s: " fmt, __func__
 
+#include <linux/component.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/device.h>
@@ -42,6 +43,8 @@ 
 
 #define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL
 
+#define BIND_TIMEOUT_MS 1000
+
 static DEFINE_MUTEX(rproc_list_mutex);
 static LIST_HEAD(rproc_list);
 
@@ -410,6 +413,45 @@  void rproc_free_vring(struct rproc_vring *rvring)
 	rsc->vring[idx].notifyid = -1;
 }
 
+static int rproc_compare_of(struct device *dev, void *data)
+{
+	if (dev->of_node)
+		return dev->of_node == data;
+	else
+		return dev == data;
+}
+
+static void rproc_release_of(struct device *dev, void *data)
+{
+	if (dev->of_node)
+		of_node_put(data);
+}
+
+static void rproc_unbind(struct device *dev)
+{
+	struct rproc *rproc = container_of(dev, struct rproc, dev);
+
+	/* undbind all child components */
+	component_unbind_all(dev, NULL);
+	complete(&rproc->completed);
+}
+
+static int rproc_bind(struct device *dev)
+{
+	struct rproc *rproc = container_of(dev, struct rproc, dev);
+
+	/* bind all child components */
+	rproc->bind_status = component_bind_all(dev, NULL);
+	complete(&rproc->completed);
+
+	return rproc->bind_status;
+}
+
+static const struct component_master_ops rproc_cmp_ops = {
+	.bind = rproc_bind,
+	.unbind = rproc_unbind,
+};
+
 /**
  * rproc_handle_vdev() - handle a vdev fw resource
  * @rproc: the remote processor
@@ -475,6 +517,10 @@  static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
 			"failed to create rproc-virtio device\n");
 		return ret;
 	}
+	/* register a component associated to the virtio platform */
+	component_match_add_release(&pdev->dev, &rproc->match,
+				    rproc_release_of, rproc_compare_of,
+				    &pdev->dev);
 	rproc->nb_vdev++;
 
 	return  0;
@@ -1318,20 +1364,51 @@  static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
 		goto clean_up_resources;
 	}
 
+	/*
+	 * bind all the children associated to the resources before starting
+	 * the remote processor. This synchro point ensures that everything is
+	 * ready to run.
+	 */
+	init_completion(&rproc->completed);
+	if (rproc->match) {
+		ret = component_master_add_with_match(dev, &rproc_cmp_ops,
+						      rproc->match);
+		if (ret) {
+			dev_err(dev, "failed to bind rproc\n");
+			goto clean_up_resources;
+		}
+	}
+	/* Wait for all children to be bound */
+	if (!wait_for_completion_timeout(&rproc->completed,
+					 msecs_to_jiffies(BIND_TIMEOUT_MS))) {
+		dev_err(dev, "failed to bind child device(s)\n");
+		ret = -ETIMEDOUT;
+		goto clean_up_resources;
+	}
+
+	ret = rproc->bind_status;
+	if (ret) {
+		dev_err(dev, "failed to bind\n");
+		goto clean_up_resources;
+	}
+
 	/* Allocate carveout resources associated to rproc */
 	ret = rproc_alloc_registered_carveouts(rproc);
 	if (ret) {
 		dev_err(dev, "Failed to allocate associated carveouts: %d\n",
 			ret);
-		goto clean_up_resources;
+		goto unbind_comp;
 	}
 
 	ret = rproc_start(rproc, fw);
 	if (ret)
-		goto clean_up_resources;
+		goto unbind_comp;
 
 	return 0;
 
+unbind_comp:
+	component_master_del(dev, &rproc_cmp_ops);
+	rproc->match = NULL;
 clean_up_resources:
 	rproc_resource_cleanup(rproc);
 	kfree(rproc->cached_table);
@@ -1733,6 +1810,21 @@  void rproc_shutdown(struct rproc *rproc)
 		goto out;
 	}
 
+	/*
+	 * Unbind all the children before cleaning resources. This synchro
+	 * point ensures that everything has been released before resources are
+	 * freed.
+	 */
+	init_completion(&rproc->completed);
+
+	component_master_del(dev, &rproc_cmp_ops);
+	rproc->match = NULL;
+
+	if (!wait_for_completion_timeout(&rproc->completed,
+					 msecs_to_jiffies(BIND_TIMEOUT_MS))) {
+		dev_err(dev, "failed to unbind child device(s)\n");
+	}
+
 	/* clean up all acquired resources */
 	rproc_resource_cleanup(rproc);
 
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index eb5bd568f11e..d7235f7356e2 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -514,6 +514,10 @@  struct rproc {
 	bool auto_boot;
 	struct list_head dump_segments;
 	int nb_vdev;
+	struct component_match *match;
+	struct completion completed;
+	int bind_status;
+
 };
 
 /**