Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Saravana Kannan and committed by
Greg Kroah-Hartman
7b337cb3 01bb86b3

+113
+98
drivers/base/core.c
··· 50 50 static DEFINE_MUTEX(wfs_lock); 51 51 static LIST_HEAD(deferred_sync); 52 52 static unsigned int defer_sync_state_count = 1; 53 + static DEFINE_MUTEX(fwnode_link_lock); 54 + 55 + /** 56 + * fwnode_link_add - Create a link between two fwnode_handles. 57 + * @con: Consumer end of the link. 58 + * @sup: Supplier end of the link. 59 + * 60 + * Create a fwnode link between fwnode handles @con and @sup. The fwnode link 61 + * represents the detail that the firmware lists @sup fwnode as supplying a 62 + * resource to @con. 63 + * 64 + * The driver core will use the fwnode link to create a device link between the 65 + * two device objects corresponding to @con and @sup when they are created. The 66 + * driver core will automatically delete the fwnode link between @con and @sup 67 + * after doing that. 68 + * 69 + * Attempts to create duplicate links between the same pair of fwnode handles 70 + * are ignored and there is no reference counting. 71 + */ 72 + int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup) 73 + { 74 + struct fwnode_link *link; 75 + int ret = 0; 76 + 77 + mutex_lock(&fwnode_link_lock); 78 + 79 + list_for_each_entry(link, &sup->consumers, s_hook) 80 + if (link->consumer == con) 81 + goto out; 82 + 83 + link = kzalloc(sizeof(*link), GFP_KERNEL); 84 + if (!link) { 85 + ret = -ENOMEM; 86 + goto out; 87 + } 88 + 89 + link->supplier = sup; 90 + INIT_LIST_HEAD(&link->s_hook); 91 + link->consumer = con; 92 + INIT_LIST_HEAD(&link->c_hook); 93 + 94 + list_add(&link->s_hook, &sup->consumers); 95 + list_add(&link->c_hook, &con->suppliers); 96 + out: 97 + mutex_unlock(&fwnode_link_lock); 98 + 99 + return ret; 100 + } 101 + 102 + /** 103 + * fwnode_links_purge_suppliers - Delete all supplier links of fwnode_handle. 104 + * @fwnode: fwnode whose supplier links need to be deleted 105 + * 106 + * Deletes all supplier links connecting directly to @fwnode. 107 + */ 108 + static void fwnode_links_purge_suppliers(struct fwnode_handle *fwnode) 109 + { 110 + struct fwnode_link *link, *tmp; 111 + 112 + mutex_lock(&fwnode_link_lock); 113 + list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook) { 114 + list_del(&link->s_hook); 115 + list_del(&link->c_hook); 116 + kfree(link); 117 + } 118 + mutex_unlock(&fwnode_link_lock); 119 + } 120 + 121 + /** 122 + * fwnode_links_purge_consumers - Delete all consumer links of fwnode_handle. 123 + * @fwnode: fwnode whose consumer links need to be deleted 124 + * 125 + * Deletes all consumer links connecting directly to @fwnode. 126 + */ 127 + static void fwnode_links_purge_consumers(struct fwnode_handle *fwnode) 128 + { 129 + struct fwnode_link *link, *tmp; 130 + 131 + mutex_lock(&fwnode_link_lock); 132 + list_for_each_entry_safe(link, tmp, &fwnode->consumers, s_hook) { 133 + list_del(&link->s_hook); 134 + list_del(&link->c_hook); 135 + kfree(link); 136 + } 137 + mutex_unlock(&fwnode_link_lock); 138 + } 139 + 140 + /** 141 + * fwnode_links_purge - Delete all links connected to a fwnode_handle. 142 + * @fwnode: fwnode whose links needs to be deleted 143 + * 144 + * Deletes all links connecting directly to a fwnode. 145 + */ 146 + void fwnode_links_purge(struct fwnode_handle *fwnode) 147 + { 148 + fwnode_links_purge_suppliers(fwnode); 149 + fwnode_links_purge_consumers(fwnode); 150 + } 53 151 54 152 #ifdef CONFIG_SRCU 55 153 static DEFINE_MUTEX(device_links_lock);
+1
drivers/of/dynamic.c
··· 356 356 357 357 property_list_free(node->properties); 358 358 property_list_free(node->deadprops); 359 + fwnode_links_purge(of_fwnode_handle(node)); 359 360 360 361 kfree(node->full_name); 361 362 kfree(node->data);
+14
include/linux/fwnode.h
··· 10 10 #define _LINUX_FWNODE_H_ 11 11 12 12 #include <linux/types.h> 13 + #include <linux/list.h> 13 14 14 15 struct fwnode_operations; 15 16 struct device; ··· 19 18 struct fwnode_handle *secondary; 20 19 const struct fwnode_operations *ops; 21 20 struct device *dev; 21 + struct list_head suppliers; 22 + struct list_head consumers; 23 + }; 24 + 25 + struct fwnode_link { 26 + struct fwnode_handle *supplier; 27 + struct list_head s_hook; 28 + struct fwnode_handle *consumer; 29 + struct list_head c_hook; 22 30 }; 23 31 24 32 /** ··· 184 174 const struct fwnode_operations *ops) 185 175 { 186 176 fwnode->ops = ops; 177 + INIT_LIST_HEAD(&fwnode->consumers); 178 + INIT_LIST_HEAD(&fwnode->suppliers); 187 179 } 188 180 189 181 extern u32 fw_devlink_get_flags(void); 182 + int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup); 183 + void fwnode_links_purge(struct fwnode_handle *fwnode); 190 184 191 185 #endif