diff mbox series

[v2,08/17] driver core: Add fwnode link support

Message ID 20201121020232.908850-9-saravanak@google.com (mailing list archive)
State Not Applicable, archived
Headers show
Series Refactor fw_devlink to significantly improve boot time | expand

Commit Message

Saravana Kannan Nov. 21, 2020, 2:02 a.m. UTC
Add support for creating supplier-consumer links between fwnodes.  It is
intended for internal use the driver core and generic firmware support
code (eg. Device Tree, ACPI), so it is simple by design and the API
provided is limited.

Signed-off-by: Saravana Kannan <saravanak@google.com>
---
 drivers/base/core.c    | 98 ++++++++++++++++++++++++++++++++++++++++++
 drivers/of/dynamic.c   |  1 +
 include/linux/fwnode.h | 14 ++++++
 3 files changed, 113 insertions(+)

Comments

Leon Romanovsky Dec. 6, 2020, 7:48 a.m. UTC | #1
On Fri, Nov 20, 2020 at 06:02:23PM -0800, Saravana Kannan wrote:
> Add support for creating supplier-consumer links between fwnodes.  It is
> intended for internal use the driver core and generic firmware support
> code (eg. Device Tree, ACPI), so it is simple by design and the API
> provided is limited.
>
> Signed-off-by: Saravana Kannan <saravanak@google.com>
> ---
>  drivers/base/core.c    | 98 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/of/dynamic.c   |  1 +
>  include/linux/fwnode.h | 14 ++++++
>  3 files changed, 113 insertions(+)
>
> diff --git a/drivers/base/core.c b/drivers/base/core.c
> index 401fa7e3505c..e2b246a44d1a 100644
> --- a/drivers/base/core.c
> +++ b/drivers/base/core.c
> @@ -50,6 +50,104 @@ static LIST_HEAD(wait_for_suppliers);
>  static DEFINE_MUTEX(wfs_lock);
>  static LIST_HEAD(deferred_sync);
>  static unsigned int defer_sync_state_count = 1;
> +static DEFINE_MUTEX(fwnode_link_lock);
> +
> +/**
> + * fwnode_link_add - Create a link between two fwnode_handles.
> + * @con: Consumer end of the link.
> + * @sup: Supplier end of the link.
> + *
> + * Create a fwnode link between fwnode handles @con and @sup. The fwnode link
> + * represents the detail that the firmware lists @sup fwnode as supplying a
> + * resource to @con.
> + *
> + * The driver core will use the fwnode link to create a device link between the
> + * two device objects corresponding to @con and @sup when they are created. The
> + * driver core will automatically delete the fwnode link between @con and @sup
> + * after doing that.
> + *
> + * Attempts to create duplicate links between the same pair of fwnode handles
> + * are ignored and there is no reference counting.

Sorry to ask, but why is that?
Isn't this a programmer error?

Thanks
Saravana Kannan Dec. 7, 2020, 7:25 p.m. UTC | #2
On Sat, Dec 5, 2020 at 11:48 PM Leon Romanovsky <leon@kernel.org> wrote:
>
> On Fri, Nov 20, 2020 at 06:02:23PM -0800, Saravana Kannan wrote:
> > Add support for creating supplier-consumer links between fwnodes.  It is
> > intended for internal use the driver core and generic firmware support
> > code (eg. Device Tree, ACPI), so it is simple by design and the API
> > provided is limited.
> >
> > Signed-off-by: Saravana Kannan <saravanak@google.com>
> > ---
> >  drivers/base/core.c    | 98 ++++++++++++++++++++++++++++++++++++++++++
> >  drivers/of/dynamic.c   |  1 +
> >  include/linux/fwnode.h | 14 ++++++
> >  3 files changed, 113 insertions(+)
> >
> > diff --git a/drivers/base/core.c b/drivers/base/core.c
> > index 401fa7e3505c..e2b246a44d1a 100644
> > --- a/drivers/base/core.c
> > +++ b/drivers/base/core.c
> > @@ -50,6 +50,104 @@ static LIST_HEAD(wait_for_suppliers);
> >  static DEFINE_MUTEX(wfs_lock);
> >  static LIST_HEAD(deferred_sync);
> >  static unsigned int defer_sync_state_count = 1;
> > +static DEFINE_MUTEX(fwnode_link_lock);
> > +
> > +/**
> > + * fwnode_link_add - Create a link between two fwnode_handles.
> > + * @con: Consumer end of the link.
> > + * @sup: Supplier end of the link.
> > + *
> > + * Create a fwnode link between fwnode handles @con and @sup. The fwnode link
> > + * represents the detail that the firmware lists @sup fwnode as supplying a
> > + * resource to @con.
> > + *
> > + * The driver core will use the fwnode link to create a device link between the
> > + * two device objects corresponding to @con and @sup when they are created. The
> > + * driver core will automatically delete the fwnode link between @con and @sup
> > + * after doing that.
> > + *
> > + * Attempts to create duplicate links between the same pair of fwnode handles
> > + * are ignored and there is no reference counting.
>
> Sorry to ask, but why is that?
> Isn't this a programmer error?

No, not a programmer error.

One firmware node can point to the same supplier many times. For
example, the consumer can be using multiple clocks from the same
supplier clock controller. In the context of fw_devlink, there's no
reason to keep track of each clock dependency separately because we'll
be creating only one device link from fwnode link. So multiple fwnode
link attempts between the same two devices are just treated as one
instance of dependency. I hope that clarifies things.

-Saravana
Leon Romanovsky Dec. 7, 2020, 7:56 p.m. UTC | #3
On Mon, Dec 07, 2020 at 11:25:03AM -0800, Saravana Kannan wrote:
> On Sat, Dec 5, 2020 at 11:48 PM Leon Romanovsky <leon@kernel.org> wrote:
> >
> > On Fri, Nov 20, 2020 at 06:02:23PM -0800, Saravana Kannan wrote:
> > > Add support for creating supplier-consumer links between fwnodes.  It is
> > > intended for internal use the driver core and generic firmware support
> > > code (eg. Device Tree, ACPI), so it is simple by design and the API
> > > provided is limited.
> > >
> > > Signed-off-by: Saravana Kannan <saravanak@google.com>
> > > ---
> > >  drivers/base/core.c    | 98 ++++++++++++++++++++++++++++++++++++++++++
> > >  drivers/of/dynamic.c   |  1 +
> > >  include/linux/fwnode.h | 14 ++++++
> > >  3 files changed, 113 insertions(+)
> > >
> > > diff --git a/drivers/base/core.c b/drivers/base/core.c
> > > index 401fa7e3505c..e2b246a44d1a 100644
> > > --- a/drivers/base/core.c
> > > +++ b/drivers/base/core.c
> > > @@ -50,6 +50,104 @@ static LIST_HEAD(wait_for_suppliers);
> > >  static DEFINE_MUTEX(wfs_lock);
> > >  static LIST_HEAD(deferred_sync);
> > >  static unsigned int defer_sync_state_count = 1;
> > > +static DEFINE_MUTEX(fwnode_link_lock);
> > > +
> > > +/**
> > > + * fwnode_link_add - Create a link between two fwnode_handles.
> > > + * @con: Consumer end of the link.
> > > + * @sup: Supplier end of the link.
> > > + *
> > > + * Create a fwnode link between fwnode handles @con and @sup. The fwnode link
> > > + * represents the detail that the firmware lists @sup fwnode as supplying a
> > > + * resource to @con.
> > > + *
> > > + * The driver core will use the fwnode link to create a device link between the
> > > + * two device objects corresponding to @con and @sup when they are created. The
> > > + * driver core will automatically delete the fwnode link between @con and @sup
> > > + * after doing that.
> > > + *
> > > + * Attempts to create duplicate links between the same pair of fwnode handles
> > > + * are ignored and there is no reference counting.
> >
> > Sorry to ask, but why is that?
> > Isn't this a programmer error?
>
> No, not a programmer error.
>
> One firmware node can point to the same supplier many times. For
> example, the consumer can be using multiple clocks from the same
> supplier clock controller. In the context of fw_devlink, there's no
> reason to keep track of each clock dependency separately because we'll
> be creating only one device link from fwnode link. So multiple fwnode
> link attempts between the same two devices are just treated as one
> instance of dependency. I hope that clarifies things.

Yes, thanks.

>
> -Saravana
Rob Herring Dec. 7, 2020, 10:21 p.m. UTC | #4
On Fri, 20 Nov 2020 18:02:23 -0800, Saravana Kannan wrote:
> Add support for creating supplier-consumer links between fwnodes.  It is
> intended for internal use the driver core and generic firmware support
> code (eg. Device Tree, ACPI), so it is simple by design and the API
> provided is limited.
> 
> Signed-off-by: Saravana Kannan <saravanak@google.com>
> ---
>  drivers/base/core.c    | 98 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/of/dynamic.c   |  1 +
>  include/linux/fwnode.h | 14 ++++++
>  3 files changed, 113 insertions(+)
> 

Acked-by: Rob Herring <robh@kernel.org>
diff mbox series

Patch

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 401fa7e3505c..e2b246a44d1a 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -50,6 +50,104 @@  static LIST_HEAD(wait_for_suppliers);
 static DEFINE_MUTEX(wfs_lock);
 static LIST_HEAD(deferred_sync);
 static unsigned int defer_sync_state_count = 1;
+static DEFINE_MUTEX(fwnode_link_lock);
+
+/**
+ * fwnode_link_add - Create a link between two fwnode_handles.
+ * @con: Consumer end of the link.
+ * @sup: Supplier end of the link.
+ *
+ * Create a fwnode link between fwnode handles @con and @sup. The fwnode link
+ * represents the detail that the firmware lists @sup fwnode as supplying a
+ * resource to @con.
+ *
+ * The driver core will use the fwnode link to create a device link between the
+ * two device objects corresponding to @con and @sup when they are created. The
+ * driver core will automatically delete the fwnode link between @con and @sup
+ * after doing that.
+ *
+ * Attempts to create duplicate links between the same pair of fwnode handles
+ * are ignored and there is no reference counting.
+ */
+int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup)
+{
+	struct fwnode_link *link;
+	int ret = 0;
+
+	mutex_lock(&fwnode_link_lock);
+
+	list_for_each_entry(link, &sup->consumers, s_hook)
+		if (link->consumer == con)
+			goto out;
+
+	link = kzalloc(sizeof(*link), GFP_KERNEL);
+	if (!link) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	link->supplier = sup;
+	INIT_LIST_HEAD(&link->s_hook);
+	link->consumer = con;
+	INIT_LIST_HEAD(&link->c_hook);
+
+	list_add(&link->s_hook, &sup->consumers);
+	list_add(&link->c_hook, &con->suppliers);
+out:
+	mutex_unlock(&fwnode_link_lock);
+
+	return ret;
+}
+
+/**
+ * fwnode_links_purge_suppliers - Delete all supplier links of fwnode_handle.
+ * @fwnode: fwnode whose supplier links need to be deleted
+ *
+ * Deletes all supplier links connecting directly to @fwnode.
+ */
+static void fwnode_links_purge_suppliers(struct fwnode_handle *fwnode)
+{
+	struct fwnode_link *link, *tmp;
+
+	mutex_lock(&fwnode_link_lock);
+	list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook) {
+		list_del(&link->s_hook);
+		list_del(&link->c_hook);
+		kfree(link);
+	}
+	mutex_unlock(&fwnode_link_lock);
+}
+
+/**
+ * fwnode_links_purge_consumers - Delete all consumer links of fwnode_handle.
+ * @fwnode: fwnode whose consumer links need to be deleted
+ *
+ * Deletes all consumer links connecting directly to @fwnode.
+ */
+static void fwnode_links_purge_consumers(struct fwnode_handle *fwnode)
+{
+	struct fwnode_link *link, *tmp;
+
+	mutex_lock(&fwnode_link_lock);
+	list_for_each_entry_safe(link, tmp, &fwnode->consumers, s_hook) {
+		list_del(&link->s_hook);
+		list_del(&link->c_hook);
+		kfree(link);
+	}
+	mutex_unlock(&fwnode_link_lock);
+}
+
+/**
+ * fwnode_links_purge - Delete all links connected to a fwnode_handle.
+ * @fwnode: fwnode whose links needs to be deleted
+ *
+ * Deletes all links connecting directly to a fwnode.
+ */
+void fwnode_links_purge(struct fwnode_handle *fwnode)
+{
+	fwnode_links_purge_suppliers(fwnode);
+	fwnode_links_purge_consumers(fwnode);
+}
 
 #ifdef CONFIG_SRCU
 static DEFINE_MUTEX(device_links_lock);
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index fe64430b438a..9a824decf61f 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -356,6 +356,7 @@  void of_node_release(struct kobject *kobj)
 
 	property_list_free(node->properties);
 	property_list_free(node->deadprops);
+	fwnode_links_purge(of_fwnode_handle(node));
 
 	kfree(node->full_name);
 	kfree(node->data);
diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h
index 5589799708b5..b88365187347 100644
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -10,6 +10,7 @@ 
 #define _LINUX_FWNODE_H_
 
 #include <linux/types.h>
+#include <linux/list.h>
 
 struct fwnode_operations;
 struct device;
@@ -18,6 +19,15 @@  struct fwnode_handle {
 	struct fwnode_handle *secondary;
 	const struct fwnode_operations *ops;
 	struct device *dev;
+	struct list_head suppliers;
+	struct list_head consumers;
+};
+
+struct fwnode_link {
+	struct fwnode_handle *supplier;
+	struct list_head s_hook;
+	struct fwnode_handle *consumer;
+	struct list_head c_hook;
 };
 
 /**
@@ -174,8 +184,12 @@  static inline void fwnode_init(struct fwnode_handle *fwnode,
 			       const struct fwnode_operations *ops)
 {
 	fwnode->ops = ops;
+	INIT_LIST_HEAD(&fwnode->consumers);
+	INIT_LIST_HEAD(&fwnode->suppliers);
 }
 
 extern u32 fw_devlink_get_flags(void);
+int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup);
+void fwnode_links_purge(struct fwnode_handle *fwnode);
 
 #endif