summaryrefslogtreecommitdiff
path: root/drivers/base/core.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-24 16:21:27 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-24 16:21:27 +0200
commit8f9aa2c90530ab92301a82231ae44f3722becd93 (patch)
treefb282e955b0a880b07131a135257fe3ec764e928 /drivers/base/core.c
parent93467b31bec6da512b51544e5e4584f2745e995e (diff)
parent155b42bec9cbb6b8cdc47dd9bd09503a81fbe493 (diff)
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r--drivers/base/core.c83
1 files changed, 74 insertions, 9 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c
index bd2ddf2aab50..478aa3fbf1e8 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -235,6 +235,79 @@ static void __fw_devlink_pickup_dangling_consumers(struct fwnode_handle *fwnode,
__fw_devlink_pickup_dangling_consumers(child, new_sup);
}
+static void fw_devlink_pickup_dangling_consumers(struct device *dev)
+{
+ struct fwnode_handle *child;
+
+ guard(mutex)(&fwnode_link_lock);
+
+ fwnode_for_each_available_child_node(dev->fwnode, child)
+ __fw_devlink_pickup_dangling_consumers(child, dev->fwnode);
+ __fw_devlink_link_to_consumers(dev);
+}
+
+/**
+ * fw_devlink_refresh_fwnode - Recheck the tree under this firmware node
+ * @fwnode: The fwnode under which the fwnode tree has changed
+ *
+ * This function is mainly meant to adjust the supplier/consumer dependencies
+ * after a fwnode tree overlay has occurred.
+ */
+void fw_devlink_refresh_fwnode(struct fwnode_handle *fwnode)
+{
+ struct device *dev;
+
+ /*
+ * Find the closest ancestor fwnode that has been converted to a device
+ * that can bind to a driver (bus device).
+ */
+ fwnode_handle_get(fwnode);
+ do {
+ if (fwnode_test_flag(fwnode, FWNODE_FLAG_NOT_DEVICE))
+ continue;
+
+ dev = get_dev_from_fwnode(fwnode);
+ if (!dev)
+ continue;
+
+ if (dev->bus)
+ break;
+
+ put_device(dev);
+ } while ((fwnode = fwnode_get_next_parent(fwnode)));
+
+ /*
+ * If none of the ancestor fwnodes have (yet) been converted to a device
+ * that can bind to a driver, there's nothing to fix up.
+ */
+ if (!fwnode)
+ return;
+
+ WARN(device_is_bound(dev) && dev->links.status != DL_DEV_DRIVER_BOUND,
+ "Don't multithread overlaying and probing the same device!\n");
+
+ /*
+ * If the device has already bound to a driver, then we need to redo
+ * some of the work that was done after the device was bound to a
+ * driver. If the device hasn't bound to a driver, running things too
+ * soon would incorrectly pick up consumers that it shouldn't.
+ */
+ if (dev->links.status == DL_DEV_DRIVER_BOUND) {
+ fw_devlink_pickup_dangling_consumers(dev);
+ /*
+ * Some of dangling consumers could have been put previously in
+ * the deferred probe list due to the unavailability of their
+ * suppliers. Those consumers have been picked up and some of
+ * their suppliers links have been updated. Time to re-try their
+ * probe sequence.
+ */
+ driver_deferred_probe_trigger();
+ }
+
+ put_device(dev);
+ fwnode_handle_put(fwnode);
+}
+
static DEFINE_MUTEX(device_links_lock);
DEFINE_STATIC_SRCU(device_links_srcu);
@@ -1312,16 +1385,8 @@ void device_links_driver_bound(struct device *dev)
* child firmware node.
*/
if (dev->fwnode && dev->fwnode->dev == dev) {
- struct fwnode_handle *child;
-
fwnode_links_purge_suppliers(dev->fwnode);
-
- guard(mutex)(&fwnode_link_lock);
-
- fwnode_for_each_available_child_node(dev->fwnode, child)
- __fw_devlink_pickup_dangling_consumers(child,
- dev->fwnode);
- __fw_devlink_link_to_consumers(dev);
+ fw_devlink_pickup_dangling_consumers(dev);
}
device_remove_file(dev, &dev_attr_waiting_for_supplier);