diff --git a/drivers/base/base.h b/drivers/base/base.h index 1b44ed588f66..e5f9b7e656c3 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -191,6 +191,7 @@ extern void device_links_driver_cleanup(struct device *dev); extern void device_links_no_driver(struct device *dev); extern bool device_links_busy(struct device *dev); extern void device_links_unbind_consumers(struct device *dev); +extern void fw_devlink_drivers_done(void); /* device pm support */ void device_pm_move_to_tail(struct device *dev); diff --git a/drivers/base/core.c b/drivers/base/core.c index ad0d26f04215..4a8bf8cda52b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -51,6 +51,7 @@ static LIST_HEAD(deferred_sync); static unsigned int defer_sync_state_count = 1; static DEFINE_MUTEX(fwnode_link_lock); static bool fw_devlink_is_permissive(void); +static bool fw_devlink_drv_reg_done; /** * fwnode_link_add - Create a link between two fwnode_handles. @@ -1598,6 +1599,52 @@ static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode) fw_devlink_parse_fwtree(child); } +static void fw_devlink_relax_link(struct device_link *link) +{ + if (!(link->flags & DL_FLAG_INFERRED)) + return; + + if (link->flags == (DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE)) + return; + + pm_runtime_drop_link(link); + link->flags = DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE; + dev_dbg(link->consumer, "Relaxing link with %s\n", + dev_name(link->supplier)); +} + +static int fw_devlink_no_driver(struct device *dev, void *data) +{ + struct device_link *link = to_devlink(dev); + + if (!link->supplier->can_match) + fw_devlink_relax_link(link); + + return 0; +} + +void fw_devlink_drivers_done(void) +{ + fw_devlink_drv_reg_done = true; + device_links_write_lock(); + class_for_each_device(&devlink_class, NULL, NULL, + fw_devlink_no_driver); + device_links_write_unlock(); +} + +static void fw_devlink_unblock_consumers(struct device *dev) +{ + struct device_link *link; + + if (!fw_devlink_flags || fw_devlink_is_permissive()) + return; + + device_links_write_lock(); + list_for_each_entry(link, &dev->links.consumers, s_node) + fw_devlink_relax_link(link); + device_links_write_unlock(); +} + /** * fw_devlink_relax_cycle - Convert cyclic links to SYNC_STATE_ONLY links * @con: Device to check dependencies for. @@ -1634,13 +1681,7 @@ static int fw_devlink_relax_cycle(struct device *con, void *sup) ret = 1; - if (!(link->flags & DL_FLAG_INFERRED)) - continue; - - pm_runtime_drop_link(link); - link->flags = DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE; - dev_dbg(link->consumer, "Relaxing link with %s\n", - dev_name(link->supplier)); + fw_devlink_relax_link(link); } return ret; } @@ -3276,6 +3317,15 @@ int device_add(struct device *dev) } bus_probe_device(dev); + + /* + * If all driver registration is done and a newly added device doesn't + * match with any driver, don't block its consumers from probing in + * case the consumer device is able to operate without this supplier. + */ + if (dev->fwnode && fw_devlink_drv_reg_done && !dev->can_match) + fw_devlink_unblock_consumers(dev); + if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 33fda3462aa0..59113543a154 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -296,6 +296,8 @@ static void deferred_probe_timeout_work_func(struct work_struct *work) { struct device_private *private, *p; + fw_devlink_drivers_done(); + driver_deferred_probe_timeout = 0; driver_deferred_probe_trigger(); flush_work(&deferred_probe_work); @@ -324,6 +326,9 @@ static int deferred_probe_initcall(void) flush_work(&deferred_probe_work); initcalls_done = true; + if (!IS_ENABLED(CONFIG_MODULES)) + fw_devlink_drivers_done(); + /* * Trigger deferred probe again, this time we won't defer anything * that is optional