diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/auxiliary.c | 11 | ||||
-rw-r--r-- | drivers/base/base.h | 1 | ||||
-rw-r--r-- | drivers/base/class.c | 2 | ||||
-rw-r--r-- | drivers/base/core.c | 559 | ||||
-rw-r--r-- | drivers/base/dd.c | 9 | ||||
-rw-r--r-- | drivers/base/devres.c | 2 | ||||
-rw-r--r-- | drivers/base/firmware_loader/fallback.c | 2 | ||||
-rw-r--r-- | drivers/base/platform.c | 474 | ||||
-rw-r--r-- | drivers/base/property.c | 52 | ||||
-rw-r--r-- | drivers/base/soc.c | 2 | ||||
-rw-r--r-- | drivers/base/swnode.c | 2 |
11 files changed, 682 insertions, 434 deletions
diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c index f303daadf843..8336535f1e11 100644 --- a/drivers/base/auxiliary.c +++ b/drivers/base/auxiliary.c @@ -92,10 +92,15 @@ static int auxiliary_bus_remove(struct device *dev) static void auxiliary_bus_shutdown(struct device *dev) { - struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver); - struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + struct auxiliary_driver *auxdrv = NULL; + struct auxiliary_device *auxdev; + + if (dev->driver) { + auxdrv = to_auxiliary_drv(dev->driver); + auxdev = to_auxiliary_dev(dev); + } - if (auxdrv->shutdown) + if (auxdrv && auxdrv->shutdown) auxdrv->shutdown(auxdev); } diff --git a/drivers/base/base.h b/drivers/base/base.h index 91cfb8405abd..f5600a83124f 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -133,7 +133,6 @@ extern void device_release_driver_internal(struct device *dev, struct device *parent); extern void driver_detach(struct device_driver *drv); -extern int driver_probe_device(struct device_driver *drv, struct device *dev); extern void driver_deferred_probe_del(struct device *dev); extern void device_set_deferred_probe_reason(const struct device *dev, struct va_format *vaf); diff --git a/drivers/base/class.c b/drivers/base/class.c index c3451481194e..7476f393df97 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -210,7 +210,7 @@ static void class_create_release(struct class *cls) } /** - * class_create - create a struct class structure + * __class_create - create a struct class structure * @owner: pointer to the module that is to "own" this struct class * @name: pointer to a string for the name of this class. * @key: the lock_class_key for this class; used by mutex lock debugging diff --git a/drivers/base/core.c b/drivers/base/core.c index d661ada1518f..25e08e5f40bd 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -46,15 +46,108 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); #endif /* Device links support. */ -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 unsigned int defer_fw_devlink_count; -static LIST_HEAD(deferred_fw_devlink); -static DEFINE_MUTEX(defer_fw_devlink_lock); +static DEFINE_MUTEX(fwnode_link_lock); static bool fw_devlink_is_permissive(void); +/** + * 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); DEFINE_STATIC_SRCU(device_links_srcu); @@ -468,7 +561,7 @@ postcore_initcall(devlink_class_init); * with runtime PM. First, setting the DL_FLAG_PM_RUNTIME flag will cause the * runtime PM framework to take the link into account. Second, if the * DL_FLAG_RPM_ACTIVE flag is set in addition to it, the supplier devices will - * be forced into the active metastate and reference-counted upon the creation + * be forced into the active meta state and reference-counted upon the creation * of the link. If DL_FLAG_PM_RUNTIME is not set, DL_FLAG_RPM_ACTIVE will be * ignored. * @@ -491,7 +584,7 @@ postcore_initcall(devlink_class_init); * Also, if DL_FLAG_STATELESS, DL_FLAG_AUTOREMOVE_CONSUMER and * DL_FLAG_AUTOREMOVE_SUPPLIER are not set in @flags (that is, a persistent * managed device link is being added), the DL_FLAG_AUTOPROBE_CONSUMER flag can - * be used to request the driver core to automaticall probe for a consmer + * be used to request the driver core to automatically probe for a consumer * driver after successfully binding a driver to the supplier device. * * The combination of DL_FLAG_STATELESS and one of DL_FLAG_AUTOREMOVE_CONSUMER, @@ -556,6 +649,17 @@ struct device_link *device_link_add(struct device *consumer, } /* + * SYNC_STATE_ONLY links are useless once a consumer device has probed. + * So, only create it if the consumer hasn't probed yet. + */ + if (flags & DL_FLAG_SYNC_STATE_ONLY && + consumer->links.status != DL_DEV_NO_DRIVER && + consumer->links.status != DL_DEV_PROBING) { + link = NULL; + goto out; + } + + /* * DL_FLAG_AUTOREMOVE_SUPPLIER indicates that the link will be needed * longer than for DL_FLAG_AUTOREMOVE_CONSUMER and setting them both * together doesn't make sense, so prefer DL_FLAG_AUTOREMOVE_SUPPLIER. @@ -697,74 +801,6 @@ out: } EXPORT_SYMBOL_GPL(device_link_add); -/** - * device_link_wait_for_supplier - Add device to wait_for_suppliers list - * @consumer: Consumer device - * - * Marks the @consumer device as waiting for suppliers to become available by - * adding it to the wait_for_suppliers list. The consumer device will never be - * probed until it's removed from the wait_for_suppliers list. - * - * The caller is responsible for adding the links to the supplier devices once - * they are available and removing the @consumer device from the - * wait_for_suppliers list once links to all the suppliers have been created. - * - * This function is NOT meant to be called from the probe function of the - * consumer but rather from code that creates/adds the consumer device. - */ -static void device_link_wait_for_supplier(struct device *consumer, - bool need_for_probe) -{ - mutex_lock(&wfs_lock); - list_add_tail(&consumer->links.needs_suppliers, &wait_for_suppliers); - consumer->links.need_for_probe = need_for_probe; - mutex_unlock(&wfs_lock); -} - -static void device_link_wait_for_mandatory_supplier(struct device *consumer) -{ - device_link_wait_for_supplier(consumer, true); -} - -static void device_link_wait_for_optional_supplier(struct device *consumer) -{ - device_link_wait_for_supplier(consumer, false); -} - -/** - * device_link_add_missing_supplier_links - Add links from consumer devices to - * supplier devices, leaving any - * consumer with inactive suppliers on - * the wait_for_suppliers list - * - * Loops through all consumers waiting on suppliers and tries to add all their - * supplier links. If that succeeds, the consumer device is removed from - * wait_for_suppliers list. Otherwise, they are left in the wait_for_suppliers - * list. Devices left on the wait_for_suppliers list will not be probed. - * - * The fwnode add_links callback is expected to return 0 if it has found and - * added all the supplier links for the consumer device. It should return an - * error if it isn't able to do so. - * - * The caller of device_link_wait_for_supplier() is expected to call this once - * it's aware of potential suppliers becoming available. - */ -static void device_link_add_missing_supplier_links(void) -{ - struct device *dev, *tmp; - - mutex_lock(&wfs_lock); - list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, - links.needs_suppliers) { - int ret = fwnode_call_int_op(dev->fwnode, add_links, dev); - if (!ret) - list_del_init(&dev->links.needs_suppliers); - else if (ret != -ENODEV || fw_devlink_is_permissive()) - dev->links.need_for_probe = false; - } - mutex_unlock(&wfs_lock); -} - #ifdef CONFIG_SRCU static void __device_link_del(struct kref *kref) { @@ -890,13 +926,13 @@ int device_links_check_suppliers(struct device *dev) * Device waiting for supplier to become available is not allowed to * probe. */ - mutex_lock(&wfs_lock); - if (!list_empty(&dev->links.needs_suppliers) && - dev->links.need_for_probe) { - mutex_unlock(&wfs_lock); + mutex_lock(&fwnode_link_lock); + if (dev->fwnode && !list_empty(&dev->fwnode->suppliers) && + !fw_devlink_is_permissive()) { + mutex_unlock(&fwnode_link_lock); return -EPROBE_DEFER; } - mutex_unlock(&wfs_lock); + mutex_unlock(&fwnode_link_lock); device_links_write_lock(); @@ -960,11 +996,11 @@ static void __device_links_queue_sync_state(struct device *dev, */ dev->state_synced = true; - if (WARN_ON(!list_empty(&dev->links.defer_hook))) + if (WARN_ON(!list_empty(&dev->links.defer_sync))) return; get_device(dev); - list_add_tail(&dev->links.defer_hook, list); + list_add_tail(&dev->links.defer_sync, list); } /** @@ -982,8 +1018,8 @@ static void device_links_flush_sync_list(struct list_head *list, { struct device *dev, *tmp; - list_for_each_entry_safe(dev, tmp, list, links.defer_hook) { - list_del_init(&dev->links.defer_hook); + list_for_each_entry_safe(dev, tmp, list, links.defer_sync) { + list_del_init(&dev->links.defer_sync); if (dev != dont_lock_dev) device_lock(dev); @@ -1021,12 +1057,12 @@ void device_links_supplier_sync_state_resume(void) if (defer_sync_state_count) goto out; - list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_hook) { + list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_sync) { /* * Delete from deferred_sync list before queuing it to - * sync_list because defer_hook is used for both lists. + * sync_list because defer_sync is used for both lists. */ - list_del_init(&dev->links.defer_hook); + list_del_init(&dev->links.defer_sync); __device_links_queue_sync_state(dev, &sync_list); } out: @@ -1044,8 +1080,8 @@ late_initcall(sync_state_resume_initcall); static void __device_links_supplier_defer_sync(struct device *sup) { - if (list_empty(&sup->links.defer_hook) && dev_has_sync_state(sup)) - list_add_tail(&sup->links.defer_hook, &deferred_sync); + if (list_empty(&sup->links.defer_sync) && dev_has_sync_state(sup)) + list_add_tail(&sup->links.defer_sync, &deferred_sync); } static void device_link_drop_managed(struct device_link *link) @@ -1062,10 +1098,7 @@ static ssize_t waiting_for_supplier_show(struct device *dev, bool val; device_lock(dev); - mutex_lock(&wfs_lock); - val = !list_empty(&dev->links.needs_suppliers) - && dev->links.need_for_probe; - mutex_unlock(&wfs_lock); + val = !list_empty(&dev->fwnode->suppliers); device_unlock(dev); return sysfs_emit(buf, "%u\n", val); } @@ -1092,9 +1125,8 @@ void device_links_driver_bound(struct device *dev) * the device links it needs to or make new device links as it needs * them. So, it no longer needs to wait on any suppliers. */ - mutex_lock(&wfs_lock); - list_del_init(&dev->links.needs_suppliers); - mutex_unlock(&wfs_lock); + if (dev->fwnode && dev->fwnode->dev == dev) + fwnode_links_purge_suppliers(dev->fwnode); device_remove_file(dev, &dev_attr_waiting_for_supplier); device_links_write_lock(); @@ -1275,7 +1307,7 @@ void device_links_driver_cleanup(struct device *dev) WRITE_ONCE(link->status, DL_STATE_DORMANT); } - list_del_init(&dev->links.defer_hook); + list_del_init(&dev->links.defer_sync); __device_links_no_driver(dev); device_links_write_unlock(); @@ -1385,10 +1417,6 @@ static void device_links_purge(struct device *dev) if (dev->class == &devlink_class) return; - mutex_lock(&wfs_lock); - list_del(&dev->links.needs_suppliers); - mutex_unlock(&wfs_lock); - /* * Delete all of the remaining links from this device to any other * devices (either consumers or suppliers). @@ -1439,139 +1467,267 @@ static bool fw_devlink_is_permissive(void) return fw_devlink_flags == DL_FLAG_SYNC_STATE_ONLY; } -static void fw_devlink_link_device(struct device *dev) +static void fw_devlink_parse_fwnode(struct fwnode_handle *fwnode) { - int fw_ret; - - if (!fw_devlink_flags) + if (fwnode->flags & FWNODE_FLAG_LINKS_ADDED) return; - mutex_lock(&defer_fw_devlink_lock); - if (!defer_fw_devlink_count) - device_link_add_missing_supplier_links(); + fwnode_call_int_op(fwnode, add_links); + fwnode->flags |= FWNODE_FLAG_LINKS_ADDED; +} + +static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode) +{ + struct fwnode_handle *child = NULL; + + fw_devlink_parse_fwnode(fwnode); + + while ((child = fwnode_get_next_available_child_node(fwnode, child))) + fw_devlink_parse_fwtree(child); +} + +/** + * fw_devlink_create_devlink - Create a device link from a consumer to fwnode + * @con - Consumer device for the device link + * @sup_handle - fwnode handle of supplier + * + * This function will try to create a device link between the consumer device + * @con and the supplier device represented by @sup_handle. + * + * The supplier has to be provided as a fwnode because incorrect cycles in + * fwnode links can sometimes cause the supplier device to never be created. + * This function detects such cases and returns an error if it cannot create a + * device link from the consumer to a missing supplier. + * + * Returns, + * 0 on successfully creating a device link + * -EINVAL if the device link cannot be created as expected + * -EAGAIN if the device link cannot be created right now, but it may be + * possible to do that in the future + */ +static int fw_devlink_create_devlink(struct device *con, + struct fwnode_handle *sup_handle, u32 flags) +{ + struct device *sup_dev; + int ret = 0; + + sup_dev = get_dev_from_fwnode(sup_handle); + if (sup_dev) { + /* + * If this fails, it is due to cycles in device links. Just + * give up on this link and treat it as invalid. + */ + if (!device_link_add(con, sup_dev, flags)) + ret = -EINVAL; + + goto out; + } /* - * The device's fwnode not having add_links() doesn't affect if other - * consumers can find this device as a supplier. So, this check is - * intentionally placed after device_link_add_missing_supplier_links(). + * DL_FLAG_SYNC_STATE_ONLY doesn't block probing and supports + * cycles. So cycle detection isn't necessary and shouldn't be + * done. */ - if (!fwnode_has_op(dev->fwnode, add_links)) - goto out; + if (flags & DL_FLAG_SYNC_STATE_ONLY) + return -EAGAIN; /* - * If fw_devlink is being deferred, assume all devices have mandatory - * suppliers they need to link to later. Then, when the fw_devlink is - * resumed, all these devices will get a chance to try and link to any - * suppliers they have. + * If we can't find the supplier device from its fwnode, it might be + * due to a cyclic dependency between fwnodes. Some of these cycles can + * be broken by applying logic. Check for these types of cycles and + * break them so that devices in the cycle probe properly. + * + * If the supplier's parent is dependent on the consumer, then + * the consumer-supplier dependency is a false dependency. So, + * treat it as an invalid link. */ - if (!defer_fw_devlink_count) { - fw_ret = fwnode_call_int_op(dev->fwnode, add_links, dev); - if (fw_ret == -ENODEV && fw_devlink_is_permissive()) - fw_ret = -EAGAIN; + sup_dev = fwnode_get_next_parent_dev(sup_handle); + if (sup_dev && device_is_dependent(con, sup_dev)) { + dev_dbg(con, "Not linking to %pfwP - False link\n", + sup_handle); + ret = -EINVAL; } else { - fw_ret = -ENODEV; /* - * defer_hook is not used to add device to deferred_sync list - * until device is bound. Since deferred fw devlink also blocks - * probing, same list hook can be used for deferred_fw_devlink. + * Can't check for cycles or no cycles. So let's try + * again later. */ - list_add_tail(&dev->links.defer_hook, &deferred_fw_devlink); + ret = -EAGAIN; } - if (fw_ret == -ENODEV) - device_link_wait_for_mandatory_supplier(dev); - else if (fw_ret) - device_link_wait_for_optional_supplier(dev); - out: - mutex_unlock(&defer_fw_devlink_lock); + put_device(sup_dev); + return ret; } /** - * fw_devlink_pause - Pause parsing of fwnode to create device links - * - * Calling this function defers any fwnode parsing to create device links until - * fw_devlink_resume() is called. Both these functions are ref counted and the - * caller needs to match the calls. - * - * While fw_devlink is paused: - * - Any device that is added won't have its fwnode parsed to create device - * links. - * - The probe of the device will also be deferred during this period. - * - Any devices that were already added, but waiting for suppliers won't be - * able to link to newly added devices. - * - * Once fw_devlink_resume(): - * - All the fwnodes that was not parsed will be parsed. - * - All the devices that were deferred probing will be reattempted if they - * aren't waiting for any more suppliers. + * __fw_devlink_link_to_consumers - Create device links to consumers of a device + * @dev - Device that needs to be linked to its consumers * - * This pair of functions, is mainly meant to optimize the parsing of fwnodes - * when a lot of devices that need to link to each other are added in a short - * interval of time. For example, adding all the top level devices in a system. + * This function looks at all the consumer fwnodes of @dev and creates device + * links between the consumer device and @dev (supplier). * - * For example, if N devices are added and: - * - All the consumers are added before their suppliers - * - All the suppliers of the N devices are part of the N devices + * If the consumer device has not been added yet, then this function creates a + * SYNC_STATE_ONLY link between @dev (supplier) and the closest ancestor device + * of the consumer fwnode. This is necessary to make sure @dev doesn't get a + * sync_state() callback before the real consumer device gets to be added and + * then probed. * - * Then: - * - * - With the use of fw_devlink_pause() and fw_devlink_resume(), each device - * will only need one parsing of its fwnode because it is guaranteed to find - * all the supplier devices already registered and ready to link to. It won't - * have to do another pass later to find one or more suppliers it couldn't - * find in the first parse of the fwnode. So, we'll only need O(N) fwnode - * parses. - * - * - Without the use of fw_devlink_pause() and fw_devlink_resume(), we would - * end up doing O(N^2) parses of fwnodes because every device that's added is - * guaranteed to trigger a parse of the fwnode of every device added before - * it. This O(N^2) parse is made worse by the fact that when a fwnode of a - * device is parsed, all it descendant devices might need to have their - * fwnodes parsed too (even if the devices themselves aren't added). + * Once device links are created from the real consumer to @dev (supplier), the + * fwnode links are deleted. */ -void fw_devlink_pause(void) +static void __fw_devlink_link_to_consumers(struct device *dev) { - mutex_lock(&defer_fw_devlink_lock); - defer_fw_devlink_count++; - mutex_unlock(&defer_fw_devlink_lock); + struct fwnode_handle *fwnode = dev->fwnode; + struct fwnode_link *link, *tmp; + + list_for_each_entry_safe(link, tmp, &fwnode->consumers, s_hook) { + u32 dl_flags = fw_devlink_get_flags(); + struct device *con_dev; + bool own_link = true; + int ret; + + con_dev = get_dev_from_fwnode(link->consumer); + /* + * If consumer device is not available yet, make a "proxy" + * SYNC_STATE_ONLY link from the consumer's parent device to + * the supplier device. This is necessary to make sure the + * supplier doesn't get a sync_state() callback before the real + * consumer can create a device link to the supplier. + * + * This proxy link step is needed to handle the case where the + * consumer's parent device is added before the supplier. + */ + if (!con_dev) { + con_dev = fwnode_get_next_parent_dev(link->consumer); + /* + * However, if the consumer's parent device is also the + * parent of the supplier, don't create a + * consumer-supplier link from the parent to its child + * device. Such a dependency is impossible. + */ + if (con_dev && + fwnode_is_ancestor_of(con_dev->fwnode, fwnode)) { + put_device(con_dev); + con_dev = NULL; + } else { + own_link = false; + dl_flags = DL_FLAG_SYNC_STATE_ONLY; + } + } + + if (!con_dev) + continue; + + ret = fw_devlink_create_devlink(con_dev, fwnode, dl_flags); + put_device(con_dev); + if (!own_link || ret == -EAGAIN) + continue; + + list_del(&link->s_hook); + list_del(&link->c_hook); + kfree(link); + } } -/** fw_devlink_resume - Resume parsing of fwnode to create device links +/** + * __fw_devlink_link_to_suppliers - Create device links to suppliers of a device + * @dev - The consumer device that needs to be linked to its suppliers + * @fwnode - Root of the fwnode tree that is used to create device links * - * This function is used in conjunction with fw_devlink_pause() and is ref - * counted. See documentation for fw_devlink_pause() for more details. + * This function looks at all the supplier fwnodes of fwnode tree rooted at + * @fwnode and creates device links between @dev (consumer) and all the + * supplier devices of the entire fwnode tree at @fwnode. + * + * The function creates normal (non-SYNC_STATE_ONLY) device links between @dev + * and the real suppliers of @dev. Once these device links are created, the + * fwnode links are deleted. When such device links are successfully created, + * this function is called recursively on those supplier devices. This is + * needed to detect and break some invalid cycles in fwnode links. See + * fw_devlink_create_devlink() for more details. + * + * In addition, it also looks at all the suppliers of the entire fwnode tree + * because some of the child devices of @dev that have not been added yet + * (because @dev hasn't probed) might already have their suppliers added to + * driver core. So, this function creates SYNC_STATE_ONLY device links between + * @dev (consumer) and these suppliers to make sure they don't execute their + * sync_state() callbacks before these child devices have a chance to create + * their device links. The fwnode links that correspond to the child devices + * aren't delete because they are needed later to create the device links + * between the real consumer and supplier devices. */ -void fw_devlink_resume(void) +static void __fw_devlink_link_to_suppliers(struct device *dev, + struct fwnode_handle *fwnode) { - struct device *dev, *tmp; - LIST_HEAD(probe_list); + bool own_link = (dev->fwnode == fwnode); + struct fwnode_link *link, *tmp; + struct fwnode_handle *child = NULL; + u32 dl_flags; - mutex_lock(&defer_fw_devlink_lock); - if (!defer_fw_devlink_count) { - WARN(true, "Unmatched fw_devlink pause/resume!"); - goto out; - } + if (own_link) + dl_flags = fw_devlink_get_flags(); + else + dl_flags = DL_FLAG_SYNC_STATE_ONLY; - defer_fw_devlink_count--; - if (defer_fw_devlink_count) - goto out; + list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook) { + int ret; + struct device *sup_dev; + struct fwnode_handle *sup = link->supplier; - device_link_add_missing_supplier_links(); - list_splice_tail_init(&deferred_fw_devlink, &probe_list); -out: - mutex_unlock(&defer_fw_devlink_lock); + ret = fw_devlink_create_devlink(dev, sup, dl_flags); + if (!own_link || ret == -EAGAIN) + continue; + + list_del(&link->s_hook); + list_del(&link->c_hook); + kfree(link); + + /* If no device link was created, nothing more to do. */ + if (ret) + continue; + + /* + * If a device link was successfully created to a supplier, we + * now need to try and link the supplier to all its suppliers. + * + * This is needed to detect and delete false dependencies in + * fwnode links that haven't been converted to a device link + * yet. See comments in fw_devlink_create_devlink() for more + * details on the false dependency. + * + * Without deleting these false dependencies, some devices will + * never probe because they'll keep waiting for their false + * dependency fwnode links to be converted to device links. + */ + sup_dev = get_dev_from_fwnode(sup); + __fw_devlink_link_to_suppliers(sup_dev, sup_dev->fwnode); + put_device(sup_dev); + } /* - * bus_probe_device() can cause new devices to get added and they'll - * try to grab defer_fw_devlink_lock. So, this needs to be done outside - * the defer_fw_devlink_lock. + * Make "proxy" SYNC_STATE_ONLY device links to represent the needs of + * all the descendants. This proxy link step is needed to handle the + * case where the supplier is added before the consumer's parent device + * (@dev). */ - list_for_each_entry_safe(dev, tmp, &probe_list, links.defer_hook) { - list_del_init(&dev->links.defer_hook); - bus_probe_device(dev); - } + while ((child = fwnode_get_next_available_child_node(fwnode, child))) + __fw_devlink_link_to_suppliers(dev, child); } + +static void fw_devlink_link_device(struct device *dev) +{ + struct fwnode_handle *fwnode = dev->fwnode; + + if (!fw_devlink_flags) + return; + + fw_devlink_parse_fwtree(fwnode); + + mutex_lock(&fwnode_link_lock); + __fw_devlink_link_to_consumers(dev); + __fw_devlink_link_to_suppliers(dev, fwnode); + mutex_unlock(&fwnode_link_lock); +} + /* Device links support end. */ int (*platform_notify)(struct device *dev) = NULL; @@ -2196,7 +2352,7 @@ static int device_add_attrs(struct device *dev) goto err_remove_dev_groups; } - if (fw_devlink_flags && !fw_devlink_is_permissive()) { + if (fw_devlink_flags && !fw_devlink_is_permissive() && dev->fwnode) { error = device_create_file(dev, &dev_attr_waiting_for_supplier); if (error) goto err_remove_dev_online; @@ -2427,8 +2583,7 @@ void device_initialize(struct device *dev) #endif INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); - INIT_LIST_HEAD(&dev->links.needs_suppliers); - INIT_LIST_HEAD(&dev->links.defer_hook); + INIT_LIST_HEAD(&dev->links.defer_sync); dev->links.status = DL_DEV_NO_DRIVER; } EXPORT_SYMBOL_GPL(device_initialize); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 148e81969e04..2f32f38a11ed 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -371,6 +371,13 @@ static void driver_bound(struct device *dev) device_pm_check_callbacks(dev); /* + * Reorder successfully probed devices to the end of the device list. + * This ensures that suspend/resume order matches probe order, which + * is usually what drivers rely on. + */ + device_pm_move_to_tail(dev); + + /* * Make sure the device is no longer in one of the deferred lists and * kick off retrying all pending devices */ @@ -717,7 +724,7 @@ EXPORT_SYMBOL_GPL(wait_for_device_probe); * * If the device has a parent, runtime-resume the parent before driver probing. */ -int driver_probe_device(struct device_driver *drv, struct device *dev) +static int driver_probe_device(struct device_driver *drv, struct device *dev) { int ret = 0; diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 586e9a75c840..fb9d5289a620 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -149,7 +149,7 @@ void * __devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp, int nid EXPORT_SYMBOL_GPL(__devres_alloc_node); #else /** - * devres_alloc - Allocate device resource data + * devres_alloc_node - Allocate device resource data * @release: Release function devres will be associated with * @size: Allocation size * @gfp: Allocation flags diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c index 4dec4b79ae06..91899d185e31 100644 --- a/drivers/base/firmware_loader/fallback.c +++ b/drivers/base/firmware_loader/fallback.c @@ -128,7 +128,7 @@ static ssize_t timeout_show(struct class *class, struct class_attribute *attr, } /** - * firmware_timeout_store() - set number of seconds to wait for firmware + * timeout_store() - set number of seconds to wait for firmware * @class: device class pointer * @attr: device attribute pointer * @buf: buffer to scan for timeout value diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 88aef93eb4dd..e9477e0bbca5 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -63,6 +63,21 @@ struct resource *platform_get_resource(struct platform_device *dev, } EXPORT_SYMBOL_GPL(platform_get_resource); +struct resource *platform_get_mem_or_io(struct platform_device *dev, + unsigned int num) +{ + u32 i; + + for (i = 0; i < dev->num_resources; i++) { + struct resource *r = &dev->resource[i]; + + if ((resource_type(r) & (IORESOURCE_MEM|IORESOURCE_IO)) && num-- == 0) + return r; + } + return NULL; +} +EXPORT_SYMBOL_GPL(platform_get_mem_or_io); + #ifdef CONFIG_HAS_IOMEM /** * devm_platform_get_and_ioremap_resource - call devm_ioremap_resource() for a @@ -743,62 +758,6 @@ err: } EXPORT_SYMBOL_GPL(platform_device_register_full); -static int platform_drv_probe(struct device *_dev) -{ - struct platform_driver *drv = to_platform_driver(_dev->driver); - struct platform_device *dev = to_platform_device(_dev); - int ret; - - ret = of_clk_set_defaults(_dev->of_node, false); - if (ret < 0) - return ret; - - ret = dev_pm_domain_attach(_dev, true); - if (ret) - goto out; - - if (drv->probe) { - ret = drv->probe(dev); - if (ret) - dev_pm_domain_detach(_dev, true); - } - -out: - if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { - dev_warn(_dev, "probe deferral not supported\n"); - ret = -ENXIO; - } - - return ret; -} - -static int platform_drv_probe_fail(struct device *_dev) -{ - return -ENXIO; -} - -static int platform_drv_remove(struct device *_dev) -{ - struct platform_driver *drv = to_platform_driver(_dev->driver); - struct platform_device *dev = to_platform_device(_dev); - int ret = 0; - - if (drv->remove) - ret = drv->remove(dev); - dev_pm_domain_detach(_dev, true); - - return ret; -} - -static void platform_drv_shutdown(struct device *_dev) -{ - struct platform_driver *drv = to_platform_driver(_dev->driver); - struct platform_device *dev = to_platform_device(_dev); - - if (drv->shutdown) - drv->shutdown(dev); -} - /** * __platform_driver_register - register a driver for platform-level devices * @drv: platform driver structure @@ -809,9 +768,6 @@ int __platform_driver_register(struct platform_driver *drv, { drv->driver.owner = owner; drv->driver.bus = &platform_bus_type; - drv->driver.probe = platform_drv_probe; - drv->driver.remove = platform_drv_remove; - drv->driver.shutdown = platform_drv_shutdown; return driver_register(&drv->driver); } @@ -827,6 +783,11 @@ void platform_driver_unregister(struct platform_driver *drv) } EXPORT_SYMBOL_GPL(platform_driver_unregister); +static int platform_probe_fail(struct platform_device *pdev) +{ + return -ENXIO; +} + /** * __platform_driver_probe - register driver for non-hotpluggable device * @drv: platform driver structure @@ -887,10 +848,9 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv, * new devices fail. */ spin_lock(&drv->driver.bus->p->klist_drivers.k_lock); - drv->probe = NULL; + drv->probe = platform_probe_fail; if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list)) retval = -ENODEV; - drv->driver.probe = platform_drv_probe_fail; spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock); if (code != retval) @@ -1017,129 +977,6 @@ void platform_unregister_drivers(struct platform_driver * const *drivers, } EXPORT_SYMBOL_GPL(platform_unregister_drivers); -/* modalias support enables more hands-off userspace setup: - * (a) environment variable lets new-style hotplug events work once system is - * fully running: "modprobe $MODALIAS" - * (b) sysfs attribute lets new-style coldplug recover from hotplug events - * mishandled before system is fully running: "modprobe $(cat modalias)" - */ -static ssize_t modalias_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - int len; - - len = of_device_modalias(dev, buf, PAGE_SIZE); - if (len != -ENODEV) - return len; - - len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); - if (len != -ENODEV) - return len; - - return sysfs_emit(buf, "platform:%s\n", pdev->name); -} -static DEVICE_ATTR_RO(modalias); - -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct platform_device *pdev = to_platform_device(dev); - char *driver_override, *old, *cp; - - /* We need to keep extra room for a newline */ - if (count >= (PAGE_SIZE - 1)) - return -EINVAL; - - driver_override = kstrndup(buf, count, GFP_KERNEL); - if (!driver_override) - return -ENOMEM; - - cp = strchr(driver_override, '\n'); - if (cp) - *cp = '\0'; - - device_lock(dev); - old = pdev->driver_override; - if (strlen(driver_override)) { - pdev->driver_override = driver_override; - } else { - kfree(driver_override); - pdev->driver_override = NULL; - } - device_unlock(dev); - - kfree(old); - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", pdev->driver_override); - device_unlock(dev); - - return len; -} -static DEVICE_ATTR_RW(driver_override); - -static ssize_t numa_node_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "%d\n", dev_to_node(dev)); -} -static DEVICE_ATTR_RO(numa_node); - -static umode_t platform_dev_attrs_visible(struct kobject *kobj, struct attribute *a, - int n) -{ - struct device *dev = container_of(kobj, typeof(*dev), kobj); - - if (a == &dev_attr_numa_node.attr && - dev_to_node(dev) == NUMA_NO_NODE) - return 0; - - return a->mode; -} - -static struct attribute *platform_dev_attrs[] = { - &dev_attr_modalias.attr, - &dev_attr_numa_node.attr, - &dev_attr_driver_override.attr, - NULL, -}; - -static struct attribute_group platform_dev_group = { - .attrs = platform_dev_attrs, - .is_visible = platform_dev_attrs_visible, -}; -__ATTRIBUTE_GROUPS(platform_dev); - -static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct platform_device *pdev = to_platform_device(dev); - int rc; - - /* Some devices have extra OF data and an OF-style MODALIAS */ - rc = of_device_uevent_modalias(dev, env); - if (rc != -ENODEV) - return rc; - - rc = acpi_device_uevent_modalias(dev, env); - if (rc != -ENODEV) - return rc; - - add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, - pdev->name); - return 0; -} - static const struct platform_device_id *platform_match_id( const struct platform_device_id *id, struct platform_device *pdev) @@ -1154,44 +991,6 @@ static const struct platform_device_id *platform_match_id( return NULL; } -/** - * platform_match - bind platform device to platform driver. - * @dev: device. - * @drv: driver. - * - * Platform device IDs are assumed to be encoded like this: - * "<name><instance>", where <name> is a short description of the type of - * device, like "pci" or "floppy", and <instance> is the enumerated - * instance of the device, like '0' or '42'. Driver IDs are simply - * "<name>". So, extract the <name> from the platform_device structure, - * and compare it against the name of the driver. Return whether they match - * or not. - */ -static int platform_match(struct device *dev, struct device_driver *drv) -{ - struct platform_device *pdev = to_platform_device(dev); - struct platform_driver *pdrv = to_platform_driver(drv); - - /* When driver_override is set, only bind to the matching driver */ - if (pdev->driver_override) - return !strcmp(pdev->driver_override, drv->name); - - /* Attempt an OF style match first */ - if (of_driver_match_device(dev, drv)) - return 1; - - /* Then try ACPI style match */ - if (acpi_driver_match_device(dev, drv)) - return 1; - - /* Then try to match against the id table */ - if (pdrv->id_table) - return platform_match_id(pdrv->id_table, pdev) != NULL; - - /* fall-back to driver name match */ - return (strcmp(pdev->name, drv->name) == 0); -} - #ifdef CONFIG_PM_SLEEP static int platform_legacy_suspend(struct device *dev, pm_message_t mesg) @@ -1336,6 +1135,234 @@ int platform_pm_restore(struct device *dev) #endif /* CONFIG_HIBERNATE_CALLBACKS */ +/* modalias support enables more hands-off userspace setup: + * (a) environment variable lets new-style hotplug events work once system is + * fully running: "modprobe $MODALIAS" + * (b) sysfs attribute lets new-style coldplug recover from hotplug events + * mishandled before system is fully running: "modprobe $(cat modalias)" + */ +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + int len; + + len = of_device_modalias(dev, buf, PAGE_SIZE); + if (len != -ENODEV) + return len; + + len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); + if (len != -ENODEV) + return len; + + return sysfs_emit(buf, "platform:%s\n", pdev->name); +} +static DEVICE_ATTR_RO(modalias); + +static ssize_t numa_node_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%d\n", dev_to_node(dev)); +} +static DEVICE_ATTR_RO(numa_node); + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + ssize_t len; + + device_lock(dev); + len = sysfs_emit(buf, "%s\n", pdev->driver_override); + device_unlock(dev); + + return len; +} + +static ssize_t driver_override_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + char *driver_override, *old, *cp; + + /* We need to keep extra room for a newline */ + if (count >= (PAGE_SIZE - 1)) + return -EINVAL; + + driver_override = kstrndup(buf, count, GFP_KERNEL); + if (!driver_override) + return -ENOMEM; + + cp = strchr(driver_override, '\n'); + if (cp) + *cp = '\0'; + + device_lock(dev); + old = pdev->driver_override; + if (strlen(driver_override)) { + pdev->driver_override = driver_override; + } else { + kfree(driver_override); + pdev->driver_override = NULL; + } + device_unlock(dev); + + kfree(old); + + return count; +} +static DEVICE_ATTR_RW(driver_override); + +static struct attribute *platform_dev_attrs[] = { + &dev_attr_modalias.attr, + &dev_attr_numa_node.attr, + &dev_attr_driver_override.attr, + NULL, +}; + +static umode_t platform_dev_attrs_visible(struct kobject *kobj, struct attribute *a, + int n) +{ + struct device *dev = container_of(kobj, typeof(*dev), kobj); + + if (a == &dev_attr_numa_node.attr && + dev_to_node(dev) == NUMA_NO_NODE) + return 0; + + return a->mode; +} + +static struct attribute_group platform_dev_group = { + .attrs = platform_dev_attrs, + .is_visible = platform_dev_attrs_visible, +}; +__ATTRIBUTE_GROUPS(platform_dev); + + +/** + * platform_match - bind platform device to platform driver. + * @dev: device. + * @drv: driver. + * + * Platform device IDs are assumed to be encoded like this: + * "<name><instance>", where <name> is a short description of the type of + * device, like "pci" or "floppy", and <instance> is the enumerated + * instance of the device, like '0' or '42'. Driver IDs are simply + * "<name>". So, extract the <name> from the platform_device structure, + * and compare it against the name of the driver. Return whether they match + * or not. + */ +static int platform_match(struct device *dev, struct device_driver *drv) +{ + struct platform_device *pdev = to_platform_device(dev); + struct platform_driver *pdrv = to_platform_driver(drv); + + /* When driver_override is set, only bind to the matching driver */ + if (pdev->driver_override) + return !strcmp(pdev->driver_override, drv->name); + + /* Attempt an OF style match first */ + if (of_driver_match_device(dev, drv)) + return 1; + + /* Then try ACPI style match */ + if (acpi_driver_match_device(dev, drv)) + return 1; + + /* Then try to match against the id table */ + if (pdrv->id_table) + return platform_match_id(pdrv->id_table, pdev) != NULL; + + /* fall-back to driver name match */ + return (strcmp(pdev->name, drv->name) == 0); +} + +static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct platform_device *pdev = to_platform_device(dev); + int rc; + + /* Some devices have extra OF data and an OF-style MODALIAS */ + rc = of_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; + + rc = acpi_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; + + add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, + pdev->name); + return 0; +} + +static int platform_probe(struct device *_dev) +{ + struct platform_driver *drv = to_platform_driver(_dev->driver); + struct platform_device *dev = to_platform_device(_dev); + int ret; + + /* + * A driver registered using platform_driver_probe() cannot be bound + * again later because the probe function usually lives in __init code + * and so is gone. For these drivers .probe is set to + * platform_probe_fail in __platform_driver_probe(). Don't even prepare + * clocks and PM domains for these to match the traditional behaviour. + */ + if (unlikely(drv->probe == platform_probe_fail)) + return -ENXIO; + + ret = of_clk_set_defaults(_dev->of_node, false); + if (ret < 0) + return ret; + + ret = dev_pm_domain_attach(_dev, true); + if (ret) + goto out; + + if (drv->probe) { + ret = drv->probe(dev); + if (ret) + dev_pm_domain_detach(_dev, true); + } + +out: + if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { + dev_warn(_dev, "probe deferral not supported\n"); + ret = -ENXIO; + } + + return ret; +} + +static int platform_remove(struct device *_dev) +{ + struct platform_driver *drv = to_platform_driver(_dev->driver); + struct platform_device *dev = to_platform_device(_dev); + int ret = 0; + + if (drv->remove) + ret = drv->remove(dev); + dev_pm_domain_detach(_dev, true); + + return ret; +} + +static void platform_shutdown(struct device *_dev) +{ + struct platform_device *dev = to_platform_device(_dev); + struct platform_driver *drv; + + if (!_dev->driver) + return; + + drv = to_platform_driver(_dev->driver); + if (drv->shutdown) + drv->shutdown(dev); +} + + int platform_dma_configure(struct device *dev) { enum dev_dma_attr attr; @@ -1362,6 +1389,9 @@ struct bus_type platform_bus_type = { .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, + .probe = platform_probe, + .remove = platform_remove, + .shutdown = platform_shutdown, .dma_configure = platform_dma_configure, .pm = &platform_dev_pm_ops, }; diff --git a/drivers/base/property.c b/drivers/base/property.c index 4c43d30145c6..35b95c6ac0c6 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -615,6 +615,31 @@ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode) EXPORT_SYMBOL_GPL(fwnode_get_next_parent); /** + * fwnode_get_next_parent_dev - Find device of closest ancestor fwnode + * @fwnode: firmware node + * + * Given a firmware node (@fwnode), this function finds its closest ancestor + * firmware node that has a corresponding struct device and returns that struct + * device. + * + * The caller of this function is expected to call put_device() on the returned + * device when they are done. + */ +struct device *fwnode_get_next_parent_dev(struct fwnode_handle *fwnode) +{ + struct device *dev = NULL; + + fwnode_handle_get(fwnode); + do { + fwnode = fwnode_get_next_parent(fwnode); + if (fwnode) + dev = get_dev_from_fwnode(fwnode); + } while (fwnode && !dev); + fwnode_handle_put(fwnode); + return dev; +} + +/** * fwnode_count_parents - Return the number of parents a node has * @fwnode: The node the parents of which are to be counted * @@ -661,6 +686,33 @@ struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwnode, EXPORT_SYMBOL_GPL(fwnode_get_nth_parent); /** + * fwnode_is_ancestor_of - Test if @test_ancestor is ancestor of @test_child + * @test_ancestor: Firmware which is tested for being an ancestor + * @test_child: Firmware which is tested for being the child + * + * A node is considered an ancestor of itself too. + * + * Returns true if @test_ancestor is an ancestor of @test_child. + * Otherwise, returns false. + */ +bool fwnode_is_ancestor_of(struct fwnode_handle *test_ancestor, + struct fwnode_handle *test_child) +{ + if (!test_ancestor) + return false; + + fwnode_handle_get(test_child); + while (test_child) { + if (test_child == test_ancestor) { + fwnode_handle_put(test_child); + return true; + } + test_child = fwnode_get_next_parent(test_child); + } + return false; +} + +/** * fwnode_get_next_child_node - Return the next child node handle for a node * @fwnode: Firmware node to find the next child node for. * @child: Handle to one of the node's child nodes or a %NULL handle. diff --git a/drivers/base/soc.c b/drivers/base/soc.c index d34609bb7386..0af5363a582c 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -168,7 +168,7 @@ out1: } EXPORT_SYMBOL_GPL(soc_device_register); -/* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */ +/* Ensure soc_dev->attr is freed after calling soc_device_unregister. */ void soc_device_unregister(struct soc_device *soc_dev) { device_unregister(&soc_dev->dev); diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 010828fc785b..4a4b2008fbc2 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -653,7 +653,7 @@ swnode_register(const struct software_node *node, struct swnode *parent, swnode->parent = parent; swnode->allocated = allocated; swnode->kobj.kset = swnode_kset; - swnode->fwnode.ops = &software_node_ops; + fwnode_init(&swnode->fwnode, &software_node_ops); ida_init(&swnode->child_ids); INIT_LIST_HEAD(&swnode->entry); |