summaryrefslogtreecommitdiff
path: root/drivers/base
diff options
context:
space:
mode:
authorSaravana Kannan <saravanak@google.com>2020-11-20 18:02:23 -0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-12-09 19:10:21 +0100
commit7b337cb3ebde384cba7405b61dfb84200bf623bf (patch)
treecf895b0d4122e4e9b0d058953a4ed2460e8e7e40 /drivers/base
parent01bb86b380a306bd937c96da36f66429f3362137 (diff)
driver core: Add fwnode link support
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. Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Saravana Kannan <saravanak@google.com> Link: https://lore.kernel.org/r/20201121020232.908850-9-saravanak@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/core.c98
1 files changed, 98 insertions, 0 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 670aa452bfe7..972d42dedfc8 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);