Commit f558b836 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'driver-core-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core

Pull driver core updates from Greg KH:
 "Here is the set of driver core patches for 5.8-rc1.

  Not all that huge this release, just a number of small fixes and
  updates:

   - software node fixes

   - kobject now sends KOBJ_REMOVE when it is removed from sysfs, not
     when it is removed from memory (which could come much later)

   - device link additions and fixes based on testing on more devices

   - firmware core cleanups

   - other minor changes, full details in the shortlog

  All have been in linux-next for a while with no reported issues"

* tag 'driver-core-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (23 commits)
  driver core: Update device link status correctly for SYNC_STATE_ONLY links
  firmware_loader: change enum fw_opt to u32
  software node: implement software_node_unregister()
  kobject: send KOBJ_REMOVE uevent when the object is removed from sysfs
  driver core: Remove unnecessary is_fwnode_dev variable in device_add()
  drivers property: When no children in primary, try secondary
  driver core: platform: Fix spelling errors in platform.c
  driver core: Remove check in driver_deferred_probe_force_trigger()
  of: platform: Batch fwnode parsing when adding all top level devices
  driver core: fw_devlink: Add support for batching fwnode parsing
  driver core: Look for waiting consumers only for a fwnode's primary device
  driver core: Move code to the right part of the file
  Revert "Revert "driver core: Set fw_devlink to "permissive" behavior by default""
  drivers: base: Fix NULL pointer exception in __platform_driver_probe() if a driver developer is foolish
  firmware_loader: move fw_fallback_config to a private kernel symbol namespace
  driver core: Add missing '\n' in log messages
  driver/base/soc: Use kobj_to_dev() API
  Add documentation on meaning of -EPROBE_DEFER
  driver core: platform: remove redundant assignment to variable ret
  debugfs: Use the correct style for SPDX License Identifier
  ...
parents 80ef846e 8c3e315d
...@@ -4,7 +4,6 @@ Device Drivers ...@@ -4,7 +4,6 @@ Device Drivers
See the kerneldoc for the struct device_driver. See the kerneldoc for the struct device_driver.
Allocation Allocation
~~~~~~~~~~ ~~~~~~~~~~
...@@ -167,9 +166,26 @@ the driver to that device. ...@@ -167,9 +166,26 @@ the driver to that device.
A driver's probe() may return a negative errno value to indicate that A driver's probe() may return a negative errno value to indicate that
the driver did not bind to this device, in which case it should have the driver did not bind to this device, in which case it should have
released all resources it allocated:: released all resources it allocated.
Optionally, probe() may return -EPROBE_DEFER if the driver depends on
resources that are not yet available (e.g., supplied by a driver that
hasn't initialized yet). The driver core will put the device onto the
deferred probe list and will try to call it again later. If a driver
must defer, it should return -EPROBE_DEFER as early as possible to
reduce the amount of time spent on setup work that will need to be
unwound and reexecuted at a later time.
.. warning::
-EPROBE_DEFER must not be returned if probe() has already created
child devices, even if those child devices are removed again
in a cleanup path. If -EPROBE_DEFER is returned after a child
device has been registered, it may result in an infinite loop of
.probe() calls to the same driver.
::
void (*sync_state)(struct device *dev); void (*sync_state) (struct device *dev);
sync_state is called only once for a device. It's called when all the consumer sync_state is called only once for a device. It's called when all the consumer
devices of the device have successfully probed. The list of consumers of the devices of the device have successfully probed. The list of consumers of the
...@@ -212,6 +228,8 @@ over management of devices from the bootloader, the usage of sync_state() is ...@@ -212,6 +228,8 @@ over management of devices from the bootloader, the usage of sync_state() is
not restricted to that. Use it whenever it makes sense to take an action after not restricted to that. Use it whenever it makes sense to take an action after
all the consumers of a device have probed:: all the consumers of a device have probed::
::
int (*remove) (struct device *dev); int (*remove) (struct device *dev);
remove is called to unbind a driver from a device. This may be remove is called to unbind a driver from a device. This may be
...@@ -224,11 +242,15 @@ not. It should free any resources allocated specifically for the ...@@ -224,11 +242,15 @@ not. It should free any resources allocated specifically for the
device; i.e. anything in the device's driver_data field. device; i.e. anything in the device's driver_data field.
If the device is still present, it should quiesce the device and place If the device is still present, it should quiesce the device and place
it into a supported low-power state:: it into a supported low-power state.
::
int (*suspend) (struct device *dev, pm_message_t state); int (*suspend) (struct device *dev, pm_message_t state);
suspend is called to put the device in a low power state:: suspend is called to put the device in a low power state.
::
int (*resume) (struct device *dev); int (*resume) (struct device *dev);
......
...@@ -153,6 +153,7 @@ extern char *make_class_name(const char *name, struct kobject *kobj); ...@@ -153,6 +153,7 @@ extern char *make_class_name(const char *name, struct kobject *kobj);
extern int devres_release_all(struct device *dev); extern int devres_release_all(struct device *dev);
extern void device_block_probing(void); extern void device_block_probing(void);
extern void device_unblock_probing(void); extern void device_unblock_probing(void);
extern void driver_deferred_probe_force_trigger(void);
/* /sys/devices directory */ /* /sys/devices directory */
extern struct kset *devices_kset; extern struct kset *devices_kset;
......
...@@ -49,6 +49,9 @@ static LIST_HEAD(wait_for_suppliers); ...@@ -49,6 +49,9 @@ static LIST_HEAD(wait_for_suppliers);
static DEFINE_MUTEX(wfs_lock); static DEFINE_MUTEX(wfs_lock);
static LIST_HEAD(deferred_sync); static LIST_HEAD(deferred_sync);
static unsigned int defer_sync_state_count = 1; static unsigned int defer_sync_state_count = 1;
static unsigned int defer_fw_devlink_count;
static DEFINE_MUTEX(defer_fw_devlink_lock);
static bool fw_devlink_is_permissive(void);
#ifdef CONFIG_SRCU #ifdef CONFIG_SRCU
static DEFINE_MUTEX(device_links_lock); static DEFINE_MUTEX(device_links_lock);
...@@ -529,7 +532,7 @@ static void device_link_add_missing_supplier_links(void) ...@@ -529,7 +532,7 @@ static void device_link_add_missing_supplier_links(void)
int ret = fwnode_call_int_op(dev->fwnode, add_links, dev); int ret = fwnode_call_int_op(dev->fwnode, add_links, dev);
if (!ret) if (!ret)
list_del_init(&dev->links.needs_suppliers); list_del_init(&dev->links.needs_suppliers);
else if (ret != -ENODEV) else if (ret != -ENODEV || fw_devlink_is_permissive())
dev->links.need_for_probe = false; dev->links.need_for_probe = false;
} }
mutex_unlock(&wfs_lock); mutex_unlock(&wfs_lock);
...@@ -643,9 +646,17 @@ static void device_links_missing_supplier(struct device *dev) ...@@ -643,9 +646,17 @@ static void device_links_missing_supplier(struct device *dev)
{ {
struct device_link *link; struct device_link *link;
list_for_each_entry(link, &dev->links.suppliers, c_node) list_for_each_entry(link, &dev->links.suppliers, c_node) {
if (link->status == DL_STATE_CONSUMER_PROBE) if (link->status != DL_STATE_CONSUMER_PROBE)
continue;
if (link->supplier->links.status == DL_DEV_DRIVER_BOUND) {
WRITE_ONCE(link->status, DL_STATE_AVAILABLE); WRITE_ONCE(link->status, DL_STATE_AVAILABLE);
} else {
WARN_ON(!(link->flags & DL_FLAG_SYNC_STATE_ONLY));
WRITE_ONCE(link->status, DL_STATE_DORMANT);
}
}
} }
/** /**
...@@ -684,11 +695,11 @@ int device_links_check_suppliers(struct device *dev) ...@@ -684,11 +695,11 @@ int device_links_check_suppliers(struct device *dev)
device_links_write_lock(); device_links_write_lock();
list_for_each_entry(link, &dev->links.suppliers, c_node) { list_for_each_entry(link, &dev->links.suppliers, c_node) {
if (!(link->flags & DL_FLAG_MANAGED) || if (!(link->flags & DL_FLAG_MANAGED))
link->flags & DL_FLAG_SYNC_STATE_ONLY)
continue; continue;
if (link->status != DL_STATE_AVAILABLE) { if (link->status != DL_STATE_AVAILABLE &&
!(link->flags & DL_FLAG_SYNC_STATE_ONLY)) {
device_links_missing_supplier(dev); device_links_missing_supplier(dev);
ret = -EPROBE_DEFER; ret = -EPROBE_DEFER;
break; break;
...@@ -949,11 +960,21 @@ static void __device_links_no_driver(struct device *dev) ...@@ -949,11 +960,21 @@ static void __device_links_no_driver(struct device *dev)
if (!(link->flags & DL_FLAG_MANAGED)) if (!(link->flags & DL_FLAG_MANAGED))
continue; continue;
if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) {
device_link_drop_managed(link); device_link_drop_managed(link);
else if (link->status == DL_STATE_CONSUMER_PROBE || continue;
link->status == DL_STATE_ACTIVE) }
if (link->status != DL_STATE_CONSUMER_PROBE &&
link->status != DL_STATE_ACTIVE)
continue;
if (link->supplier->links.status == DL_DEV_DRIVER_BOUND) {
WRITE_ONCE(link->status, DL_STATE_AVAILABLE); WRITE_ONCE(link->status, DL_STATE_AVAILABLE);
} else {
WARN_ON(!(link->flags & DL_FLAG_SYNC_STATE_ONLY));
WRITE_ONCE(link->status, DL_STATE_DORMANT);
}
} }
dev->links.status = DL_DEV_NO_DRIVER; dev->links.status = DL_DEV_NO_DRIVER;
...@@ -1162,6 +1183,150 @@ static void device_links_purge(struct device *dev) ...@@ -1162,6 +1183,150 @@ static void device_links_purge(struct device *dev)
device_links_write_unlock(); device_links_write_unlock();
} }
static u32 fw_devlink_flags = DL_FLAG_SYNC_STATE_ONLY;
static int __init fw_devlink_setup(char *arg)
{
if (!arg)
return -EINVAL;
if (strcmp(arg, "off") == 0) {
fw_devlink_flags = 0;
} else if (strcmp(arg, "permissive") == 0) {
fw_devlink_flags = DL_FLAG_SYNC_STATE_ONLY;
} else if (strcmp(arg, "on") == 0) {
fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER;
} else if (strcmp(arg, "rpm") == 0) {
fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER |
DL_FLAG_PM_RUNTIME;
}
return 0;
}
early_param("fw_devlink", fw_devlink_setup);
u32 fw_devlink_get_flags(void)
{
return fw_devlink_flags;
}
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)
{
int fw_ret;
if (!fw_devlink_flags)
return;
mutex_lock(&defer_fw_devlink_lock);
if (!defer_fw_devlink_count)
device_link_add_missing_supplier_links();
/*
* 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().
*/
if (!fwnode_has_op(dev->fwnode, add_links))
goto out;
/*
* 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 (!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;
} else {
fw_ret = -ENODEV;
}
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);
}
/**
* 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.
*
* 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.
*
* 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
*
* 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).
*/
void fw_devlink_pause(void)
{
mutex_lock(&defer_fw_devlink_lock);
defer_fw_devlink_count++;
mutex_unlock(&defer_fw_devlink_lock);
}
/** fw_devlink_resume - Resume parsing of fwnode 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.
*/
void fw_devlink_resume(void)
{
mutex_lock(&defer_fw_devlink_lock);
if (!defer_fw_devlink_count) {
WARN(true, "Unmatched fw_devlink pause/resume!");
goto out;
}
defer_fw_devlink_count--;
if (defer_fw_devlink_count)
goto out;
device_link_add_missing_supplier_links();
driver_deferred_probe_force_trigger();
out:
mutex_unlock(&defer_fw_devlink_lock);
}
/* Device links support end. */ /* Device links support end. */
int (*platform_notify)(struct device *dev) = NULL; int (*platform_notify)(struct device *dev) = NULL;
...@@ -2364,36 +2529,6 @@ static int device_private_init(struct device *dev) ...@@ -2364,36 +2529,6 @@ static int device_private_init(struct device *dev)
return 0; return 0;
} }
static u32 fw_devlink_flags;
static int __init fw_devlink_setup(char *arg)
{
if (!arg)
return -EINVAL;
if (strcmp(arg, "off") == 0) {
fw_devlink_flags = 0;
} else if (strcmp(arg, "permissive") == 0) {
fw_devlink_flags = DL_FLAG_SYNC_STATE_ONLY;
} else if (strcmp(arg, "on") == 0) {
fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER;
} else if (strcmp(arg, "rpm") == 0) {
fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER |
DL_FLAG_PM_RUNTIME;
}
return 0;
}
early_param("fw_devlink", fw_devlink_setup);
u32 fw_devlink_get_flags(void)
{
return fw_devlink_flags;
}
static bool fw_devlink_is_permissive(void)
{
return fw_devlink_flags == DL_FLAG_SYNC_STATE_ONLY;
}
/** /**
* device_add - add device to device hierarchy. * device_add - add device to device hierarchy.
* @dev: device. * @dev: device.
...@@ -2426,9 +2561,8 @@ int device_add(struct device *dev) ...@@ -2426,9 +2561,8 @@ int device_add(struct device *dev)
struct device *parent; struct device *parent;
struct kobject *kobj; struct kobject *kobj;
struct class_interface *class_intf; struct class_interface *class_intf;
int error = -EINVAL, fw_ret; int error = -EINVAL;
struct kobject *glue_dir = NULL; struct kobject *glue_dir = NULL;
bool is_fwnode_dev = false;
dev = get_device(dev); dev = get_device(dev);
if (!dev) if (!dev)
...@@ -2526,11 +2660,6 @@ int device_add(struct device *dev) ...@@ -2526,11 +2660,6 @@ int device_add(struct device *dev)
kobject_uevent(&dev->kobj, KOBJ_ADD); kobject_uevent(&dev->kobj, KOBJ_ADD);
if (dev->fwnode && !dev->fwnode->dev) {
dev->fwnode->dev = dev;
is_fwnode_dev = true;
}
/* /*
* Check if any of the other devices (consumers) have been waiting for * Check if any of the other devices (consumers) have been waiting for
* this device (supplier) to be added so that they can create a device * this device (supplier) to be added so that they can create a device
...@@ -2539,19 +2668,13 @@ int device_add(struct device *dev) ...@@ -2539,19 +2668,13 @@ int device_add(struct device *dev)
* This needs to happen after device_pm_add() because device_link_add() * This needs to happen after device_pm_add() because device_link_add()
* requires the supplier be registered before it's called. * requires the supplier be registered before it's called.
* *
* But this also needs to happe before bus_probe_device() to make sure * But this also needs to happen before bus_probe_device() to make sure
* waiting consumers can link to it before the driver is bound to the * waiting consumers can link to it before the driver is bound to the
* device and the driver sync_state callback is called for this device. * device and the driver sync_state callback is called for this device.
*/ */
device_link_add_missing_supplier_links(); if (dev->fwnode && !dev->fwnode->dev) {
dev->fwnode->dev = dev;
if (fw_devlink_flags && is_fwnode_dev && fw_devlink_link_device(dev);
fwnode_has_op(dev->fwnode, add_links)) {
fw_ret = fwnode_call_int_op(dev->fwnode, add_links, dev);
if (fw_ret == -ENODEV && !fw_devlink_is_permissive())
device_link_wait_for_mandatory_supplier(dev);
else if (fw_ret)
device_link_wait_for_optional_supplier(dev);
} }
bus_probe_device(dev); bus_probe_device(dev);
......
...@@ -164,6 +164,11 @@ static void driver_deferred_probe_trigger(void) ...@@ -164,6 +164,11 @@ static void driver_deferred_probe_trigger(void)
if (!driver_deferred_probe_enable) if (!driver_deferred_probe_enable)
return; return;
driver_deferred_probe_force_trigger();
}
void driver_deferred_probe_force_trigger(void)
{
/* /*
* A successful probe means that all the devices in the pending list * A successful probe means that all the devices in the pending list
* should be triggered to be reprobed. Move all the deferred devices * should be triggered to be reprobed. Move all the deferred devices
...@@ -254,12 +259,12 @@ __setup("deferred_probe_timeout=", deferred_probe_timeout_setup); ...@@ -254,12 +259,12 @@ __setup("deferred_probe_timeout=", deferred_probe_timeout_setup);
int driver_deferred_probe_check_state(struct device *dev) int driver_deferred_probe_check_state(struct device *dev)
{ {
if (!IS_ENABLED(CONFIG_MODULES) && initcalls_done) { if (!IS_ENABLED(CONFIG_MODULES) && initcalls_done) {
dev_warn(dev, "ignoring dependency for device, assuming no driver"); dev_warn(dev, "ignoring dependency for device, assuming no driver\n");
return -ENODEV; return -ENODEV;
} }
if (!driver_deferred_probe_timeout && initcalls_done) { if (!driver_deferred_probe_timeout && initcalls_done) {
dev_warn(dev, "deferred probe timeout, ignoring dependency"); dev_warn(dev, "deferred probe timeout, ignoring dependency\n");
return -ETIMEDOUT; return -ETIMEDOUT;
} }
...@@ -275,7 +280,7 @@ static void deferred_probe_timeout_work_func(struct work_struct *work) ...@@ -275,7 +280,7 @@ static void deferred_probe_timeout_work_func(struct work_struct *work)
flush_work(&deferred_probe_work); flush_work(&deferred_probe_work);
list_for_each_entry_safe(private, p, &deferred_probe_pending_list, deferred_probe) list_for_each_entry_safe(private, p, &deferred_probe_pending_list, deferred_probe)
dev_info(private->device, "deferred probe pending"); dev_info(private->device, "deferred probe pending\n");
wake_up(&probe_timeout_waitqueue); wake_up(&probe_timeout_waitqueue);
} }
static DECLARE_DELAYED_WORK(deferred_probe_timeout_work, deferred_probe_timeout_work_func); static DECLARE_DELAYED_WORK(deferred_probe_timeout_work, deferred_probe_timeout_work_func);
...@@ -336,7 +341,7 @@ bool device_is_bound(struct device *dev) ...@@ -336,7 +341,7 @@ bool device_is_bound(struct device *dev)
static void driver_bound(struct device *dev) static void driver_bound(struct device *dev)
{ {
if (device_is_bound(dev)) { if (device_is_bound(dev)) {
printk(KERN_WARNING "%s: device %s already bound\n", pr_warn("%s: device %s already bound\n",
__func__, kobject_name(&dev->kobj)); __func__, kobject_name(&dev->kobj));
return; return;
} }
...@@ -505,7 +510,7 @@ static int really_probe(struct device *dev, struct device_driver *drv) ...@@ -505,7 +510,7 @@ static int really_probe(struct device *dev, struct device_driver *drv)
} }
if (driver_sysfs_add(dev)) { if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", pr_err("%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev)); __func__, dev_name(dev));
goto probe_failed; goto probe_failed;
} }
...@@ -597,8 +602,7 @@ static int really_probe(struct device *dev, struct device_driver *drv) ...@@ -597,8 +602,7 @@ static int really_probe(struct device *dev, struct device_driver *drv)
break; break;
default: default:
/* driver matched but the probe failed */ /* driver matched but the probe failed */
printk(KERN_WARNING pr_warn("%s: probe of %s failed with error %d\n",
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret); drv->name, dev_name(dev), ret);
} }
/* /*
...@@ -624,7 +628,7 @@ static int really_probe_debug(struct device *dev, struct device_driver *drv) ...@@ -624,7 +628,7 @@ static int really_probe_debug(struct device *dev, struct device_driver *drv)
ret = really_probe(dev, drv); ret = really_probe(dev, drv);
rettime = ktime_get(); rettime = ktime_get();
delta = ktime_sub(rettime, calltime); delta = ktime_sub(rettime, calltime);
printk(KERN_DEBUG "probe of %s returned %d after %lld usecs\n", pr_debug("probe of %s returned %d after %lld usecs\n",
dev_name(dev), ret, (s64) ktime_to_us(delta)); dev_name(dev), ret, (s64) ktime_to_us(delta));
return ret; return ret;
} }
...@@ -713,8 +717,7 @@ static inline bool cmdline_requested_async_probing(const char *drv_name) ...@@ -713,8 +717,7 @@ static inline bool cmdline_requested_async_probing(const char *drv_name)
static int __init save_async_options(char *buf) static int __init save_async_options(char *buf)
{ {
if (strlen(buf) >= ASYNC_DRV_NAMES_MAX_LEN) if (strlen(buf) >= ASYNC_DRV_NAMES_MAX_LEN)
printk(KERN_WARNING pr_warn("Too long list of driver names for 'driver_async_probe'!\n");
"Too long list of driver names for 'driver_async_probe'!\n");
strlcpy(async_probe_drv_names, buf, ASYNC_DRV_NAMES_MAX_LEN); strlcpy(async_probe_drv_names, buf, ASYNC_DRV_NAMES_MAX_LEN);
return 0; return 0;
...@@ -789,7 +792,7 @@ static int __device_attach_driver(struct device_driver *drv, void *_data) ...@@ -789,7 +792,7 @@ static int __device_attach_driver(struct device_driver *drv, void *_data)
dev_dbg(dev, "Device match requests probe deferral\n"); dev_dbg(dev, "Device match requests probe deferral\n");
driver_deferred_probe_add(dev); driver_deferred_probe_add(dev);
} else if (ret < 0) { } else if (ret < 0) {
dev_dbg(dev, "Bus failed to match device: %d", ret); dev_dbg(dev, "Bus failed to match device: %d\n", ret);
return ret; return ret;
} /* ret > 0 means positive match */ } /* ret > 0 means positive match */
...@@ -1022,7 +1025,7 @@ static int __driver_attach(struct device *dev, void *data) ...@@ -1022,7 +1025,7 @@ static int __driver_attach(struct device *dev, void *data)
dev_dbg(dev, "Device match requests probe deferral\n"); dev_dbg(dev, "Device match requests probe deferral\n");
driver_deferred_probe_add(dev); driver_deferred_probe_add(dev);
} else if (ret < 0) { } else if (ret < 0) {
dev_dbg(dev, "Bus failed to match device: %d", ret); dev_dbg(dev, "Bus failed to match device: %d\n", ret);
return ret; return ret;
} /* ret > 0 means positive match */ } /* ret > 0 means positive match */
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/umh.h> #include <linux/umh.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/module.h>
#include "fallback.h" #include "fallback.h"
#include "firmware.h" #include "firmware.h"
...@@ -17,6 +18,8 @@ ...@@ -17,6 +18,8 @@
* firmware fallback mechanism * firmware fallback mechanism
*/ */
MODULE_IMPORT_NS(FIRMWARE_LOADER_PRIVATE);
extern struct firmware_fallback_config fw_fallback_config; extern struct firmware_fallback_config fw_fallback_config;
/* These getters are vetted to use int properly */ /* These getters are vetted to use int properly */
...@@ -460,7 +463,7 @@ static const struct attribute_group *fw_dev_attr_groups[] = { ...@@ -460,7 +463,7 @@ static const struct attribute_group *fw_dev_attr_groups[] = {
static struct fw_sysfs * static struct fw_sysfs *
fw_create_instance(struct firmware *firmware, const char *fw_name, fw_create_instance(struct firmware *firmware, const char *fw_name,
struct device *device, enum fw_opt opt_flags) struct device *device, u32 opt_flags)
{ {
struct fw_sysfs *fw_sysfs; struct fw_sysfs *fw_sysfs;
struct device *f_dev; struct device *f_dev;
...@@ -493,7 +496,7 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, ...@@ -493,7 +496,7 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,
* In charge of constructing a sysfs fallback interface for firmware loading. * In charge of constructing a sysfs fallback interface for firmware loading.
**/ **/
static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs,
enum fw_opt opt_flags, long timeout) u32 opt_flags, long timeout)
{ {
int retval = 0; int retval = 0;
struct device *f_dev = &fw_sysfs->dev; struct device *f_dev = &fw_sysfs->dev;
...@@ -547,7 +550,7 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, ...@@ -547,7 +550,7 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs,
static int fw_load_from_user_helper(struct firmware *firmware, static int fw_load_from_user_helper(struct firmware *firmware,
const char *name, struct device *device, const char *name, struct device *device,
enum fw_opt opt_flags) u32 opt_flags)
{ {
struct fw_sysfs *fw_sysfs; struct fw_sysfs *fw_sysfs;
long timeout; long timeout;
...@@ -588,7 +591,7 @@ static int fw_load_from_user_helper(struct firmware *firmware, ...@@ -588,7 +591,7 @@ static int fw_load_from_user_helper(struct firmware *firmware,
return ret; return ret;
} }
static bool fw_force_sysfs_fallback(enum fw_opt opt_flags) static bool fw_force_sysfs_fallback(u32 opt_flags)
{ {
if (fw_fallback_config.force_sysfs_fallback) if (fw_fallback_config.force_sysfs_fallback)
return true; return true;
...@@ -597,7 +600,7 @@ static bool fw_force_sysfs_fallback(enum fw_opt opt_flags) ...@@ -597,7 +600,7 @@ static bool fw_force_sysfs_fallback(enum fw_opt opt_flags)
return true; return true;
} }
static bool fw_run_sysfs_fallback(enum fw_opt opt_flags) static bool fw_run_sysfs_fallback(u32 opt_flags)
{ {
int ret; int ret;
...@@ -640,7 +643,7 @@ static bool fw_run_sysfs_fallback(enum fw_opt opt_flags) ...@@ -640,7 +643,7 @@ static bool fw_run_sysfs_fallback(enum fw_opt opt_flags)
**/ **/
int firmware_fallback_sysfs(struct firmware *fw, const char *name, int firmware_fallback_sysfs(struct firmware *fw, const char *name,
struct device *device, struct device *device,
enum fw_opt opt_flags, u32 opt_flags,
int ret) int ret)
{ {
if (!fw_run_sysfs_fallback(opt_flags)) if (!fw_run_sysfs_fallback(opt_flags))
......
...@@ -33,7 +33,7 @@ struct firmware_fallback_config { ...@@ -33,7 +33,7 @@ struct firmware_fallback_config {
#ifdef CONFIG_FW_LOADER_USER_HELPER #ifdef CONFIG_FW_LOADER_USER_HELPER
int firmware_fallback_sysfs(struct firmware *fw, const char *name, int firmware_fallback_sysfs(struct firmware *fw, const char *name,
struct device *device, struct device *device,
enum fw_opt opt_flags, u32 opt_flags,
int ret); int ret);
void kill_pending_fw_fallback_reqs(bool only_kill_custom); void kill_pending_fw_fallback_reqs(bool only_kill_custom);
...@@ -45,7 +45,7 @@ void unregister_sysfs_loader(void); ...@@ -45,7 +45,7 @@ void unregister_sysfs_loader(void);
#else /* CONFIG_FW_LOADER_USER_HELPER */ #else /* CONFIG_FW_LOADER_USER_HELPER */
static inline int firmware_fallback_sysfs(struct firmware *fw, const char *name, static inline int firmware_fallback_sysfs(struct firmware *fw, const char *name,
struct device *device, struct device *device,
enum fw_opt opt_flags, u32 opt_flags,
int ret) int ret)
{ {
/* Keep carrying over the same error */ /* Keep carrying over the same error */
...@@ -67,10 +67,10 @@ static inline void unregister_sysfs_loader(void) ...@@ -67,10 +67,10 @@ static inline void unregister_sysfs_loader(void)
#endif /* CONFIG_FW_LOADER_USER_HELPER */ #endif /* CONFIG_FW_LOADER_USER_HELPER */
#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
int firmware_fallback_platform(struct fw_priv *fw_priv, enum fw_opt opt_flags); int firmware_fallback_platform(struct fw_priv *fw_priv, u32 opt_flags);
#else #else
static inline int firmware_fallback_platform(struct fw_priv *fw_priv, static inline int firmware_fallback_platform(struct fw_priv *fw_priv,
enum fw_opt opt_flags) u32 opt_flags)
{ {
return -ENOENT; return -ENOENT;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#include "fallback.h" #include "fallback.h"
#include "firmware.h" #include "firmware.h"
int firmware_fallback_platform(struct fw_priv *fw_priv, enum fw_opt opt_flags) int firmware_fallback_platform(struct fw_priv *fw_priv, u32 opt_flags)
{ {
const u8 *data; const u8 *data;
size_t size; size_t size;
......
...@@ -21,7 +21,7 @@ struct firmware_fallback_config fw_fallback_config = { ...@@ -21,7 +21,7 @@ struct firmware_fallback_config fw_fallback_config = {
.loading_timeout = 60, .loading_timeout = 60,
.old_timeout = 60, .old_timeout = 60,
}; };
EXPORT_SYMBOL_GPL(fw_fallback_config); EXPORT_SYMBOL_NS_GPL(fw_fallback_config, FIRMWARE_LOADER_PRIVATE);
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
struct ctl_table firmware_config_table[] = { struct ctl_table firmware_config_table[] = {
......
...@@ -136,8 +136,7 @@ static inline void fw_state_done(struct fw_priv *fw_priv) ...@@ -136,8 +136,7 @@ static inline void fw_state_done(struct fw_priv *fw_priv)
__fw_state_set(fw_priv, FW_STATUS_DONE); __fw_state_set(fw_priv, FW_STATUS_DONE);
} }
int assign_fw(struct firmware *fw, struct device *device, int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags);
enum fw_opt opt_flags);
#ifdef CONFIG_FW_LOADER_PAGED_BUF #ifdef CONFIG_FW_LOADER_PAGED_BUF
void fw_free_paged_buf(struct fw_priv *fw_priv); void fw_free_paged_buf(struct fw_priv *fw_priv);
......
...@@ -210,7 +210,7 @@ static struct fw_priv *__lookup_fw_priv(const char *fw_name) ...@@ -210,7 +210,7 @@ static struct fw_priv *__lookup_fw_priv(const char *fw_name)
static int alloc_lookup_fw_priv(const char *fw_name, static int alloc_lookup_fw_priv(const char *fw_name,
struct firmware_cache *fwc, struct firmware_cache *fwc,
struct fw_priv **fw_priv, void *dbuf, struct fw_priv **fw_priv, void *dbuf,
size_t size, enum fw_opt opt_flags) size_t size, u32 opt_flags)
{ {
struct fw_priv *tmp; struct fw_priv *tmp;
...@@ -548,9 +548,6 @@ static void firmware_free_data(const struct firmware *fw) ...@@ -548,9 +548,6 @@ static void firmware_free_data(const struct firmware *fw)
static void fw_set_page_data(struct fw_priv *fw_priv, struct firmware *fw) static void fw_set_page_data(struct fw_priv *fw_priv, struct firmware *fw)
{ {
fw->priv = fw_priv; fw->priv = fw_priv;
#ifdef CONFIG_FW_LOADER_USER_HELPER
fw->pages = fw_priv->pages;
#endif
fw->size = fw_priv->size; fw->size = fw_priv->size;
fw->data = fw_priv->data; fw->data = fw_priv->data;
...@@ -635,8 +632,7 @@ static int fw_add_devm_name(struct device *dev, const char *name) ...@@ -635,8 +632,7 @@ static int fw_add_devm_name(struct device *dev, const char *name)
} }
#endif #endif
int assign_fw(struct firmware *fw, struct device *device, int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags)
enum fw_opt opt_flags)
{ {
struct fw_priv *fw_priv = fw->priv; struct fw_priv *fw_priv = fw->priv;
int ret; int ret;
...@@ -687,7 +683,7 @@ int assign_fw(struct firmware *fw, struct device *device, ...@@ -687,7 +683,7 @@ int assign_fw(struct firmware *fw, struct device *device,
static int static int
_request_firmware_prepare(struct firmware **firmware_p, const char *name, _request_firmware_prepare(struct firmware **firmware_p, const char *name,
struct device *device, void *dbuf, size_t size, struct device *device, void *dbuf, size_t size,
enum fw_opt opt_flags) u32 opt_flags)
{ {
struct firmware *firmware; struct firmware *firmware;
struct fw_priv *fw_priv; struct fw_priv *fw_priv;
...@@ -753,7 +749,7 @@ static void fw_abort_batch_reqs(struct firmware *fw) ...@@ -753,7 +749,7 @@ static void fw_abort_batch_reqs(struct firmware *fw)
static int static int
_request_firmware(const struct firmware **firmware_p, const char *name, _request_firmware(const struct firmware **firmware_p, const char *name,
struct device *device, void *buf, size_t size, struct device *device, void *buf, size_t size,
enum fw_opt opt_flags) u32 opt_flags)
{ {
struct firmware *fw = NULL; struct firmware *fw = NULL;
int ret; int ret;
...@@ -990,7 +986,7 @@ struct firmware_work { ...@@ -990,7 +986,7 @@ struct firmware_work {
struct device *device; struct device *device;
void *context; void *context;
void (*cont)(const struct firmware *fw, void *context); void (*cont)(const struct firmware *fw, void *context);
enum fw_opt opt_flags; u32 opt_flags;
}; };
static void request_firmware_work_func(struct work_struct *work) static void request_firmware_work_func(struct work_struct *work)
......
...@@ -682,7 +682,7 @@ EXPORT_SYMBOL_GPL(platform_device_unregister); ...@@ -682,7 +682,7 @@ EXPORT_SYMBOL_GPL(platform_device_unregister);
struct platform_device *platform_device_register_full( struct platform_device *platform_device_register_full(
const struct platform_device_info *pdevinfo) const struct platform_device_info *pdevinfo)
{ {
int ret = -ENOMEM; int ret;
struct platform_device *pdev; struct platform_device *pdev;
pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id); pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id);
...@@ -863,6 +863,8 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv, ...@@ -863,6 +863,8 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv,
/* temporary section violation during probe() */ /* temporary section violation during probe() */
drv->probe = probe; drv->probe = probe;
retval = code = __platform_driver_register(drv, module); retval = code = __platform_driver_register(drv, module);
if (retval)
return retval;
/* /*
* Fixup that section violation, being paranoid about code scanning * Fixup that section violation, being paranoid about code scanning
...@@ -987,7 +989,7 @@ EXPORT_SYMBOL_GPL(__platform_register_drivers); ...@@ -987,7 +989,7 @@ EXPORT_SYMBOL_GPL(__platform_register_drivers);
* @drivers: an array of drivers to unregister * @drivers: an array of drivers to unregister
* @count: the number of drivers to unregister * @count: the number of drivers to unregister
* *
* Unegisters platform drivers specified by an array. This is typically used * Unregisters platform drivers specified by an array. This is typically used
* to complement an earlier call to platform_register_drivers(). Drivers are * to complement an earlier call to platform_register_drivers(). Drivers are
* unregistered in the reverse order in which they were registered. * unregistered in the reverse order in which they were registered.
*/ */
......
...@@ -708,14 +708,23 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev, ...@@ -708,14 +708,23 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev,
struct fwnode_handle *child) struct fwnode_handle *child)
{ {
struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_device *adev = ACPI_COMPANION(dev);
struct fwnode_handle *fwnode = NULL; struct fwnode_handle *fwnode = NULL, *next;
if (dev->of_node) if (dev->of_node)
fwnode = &dev->of_node->fwnode; fwnode = &dev->of_node->fwnode;
else if (adev) else if (adev)
fwnode = acpi_fwnode_handle(adev); fwnode = acpi_fwnode_handle(adev);
return fwnode_get_next_child_node(fwnode, child); /* Try to find a child in primary fwnode */
next = fwnode_get_next_child_node(fwnode, child);
if (next)
return next;
/* When no more children in primary, continue with secondary */
if (!IS_ERR_OR_NULL(fwnode->secondary))
next = fwnode_get_next_child_node(fwnode->secondary, child);
return next;
} }
EXPORT_SYMBOL_GPL(device_get_next_child_node); EXPORT_SYMBOL_GPL(device_get_next_child_node);
......
...@@ -46,7 +46,7 @@ static umode_t soc_attribute_mode(struct kobject *kobj, ...@@ -46,7 +46,7 @@ static umode_t soc_attribute_mode(struct kobject *kobj,
struct attribute *attr, struct attribute *attr,
int index) int index)
{ {
struct device *dev = container_of(kobj, struct device, kobj); struct device *dev = kobj_to_dev(kobj);
struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
if ((attr == &dev_attr_machine.attr) if ((attr == &dev_attr_machine.attr)
......
...@@ -712,17 +712,18 @@ EXPORT_SYMBOL_GPL(software_node_register_nodes); ...@@ -712,17 +712,18 @@ EXPORT_SYMBOL_GPL(software_node_register_nodes);
* @nodes: Zero terminated array of software nodes to be unregistered * @nodes: Zero terminated array of software nodes to be unregistered
* *
* Unregister multiple software nodes at once. * Unregister multiple software nodes at once.
*
* NOTE: Be careful using this call if the nodes had parent pointers set up in
* them before registering. If so, it is wiser to remove the nodes
* individually, in the correct order (child before parent) instead of relying
* on the sequential order of the list of nodes in the array.
*/ */
void software_node_unregister_nodes(const struct software_node *nodes) void software_node_unregister_nodes(const struct software_node *nodes)
{ {
struct swnode *swnode;
int i; int i;
for (i = 0; nodes[i].name; i++) { for (i = 0; nodes[i].name; i++)
swnode = software_node_to_swnode(&nodes[i]); software_node_unregister(&nodes[i]);
if (swnode)
fwnode_remove_software_node(&swnode->fwnode);
}
} }
EXPORT_SYMBOL_GPL(software_node_unregister_nodes); EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
...@@ -789,6 +790,20 @@ int software_node_register(const struct software_node *node) ...@@ -789,6 +790,20 @@ int software_node_register(const struct software_node *node)
} }
EXPORT_SYMBOL_GPL(software_node_register); EXPORT_SYMBOL_GPL(software_node_register);
/**
* software_node_unregister - Unregister static software node
* @node: The software node to be unregistered
*/
void software_node_unregister(const struct software_node *node)
{
struct swnode *swnode;
swnode = software_node_to_swnode(node);
if (swnode)
fwnode_remove_software_node(&swnode->fwnode);
}
EXPORT_SYMBOL_GPL(software_node_unregister);
struct fwnode_handle * struct fwnode_handle *
fwnode_create_software_node(const struct property_entry *properties, fwnode_create_software_node(const struct property_entry *properties,
const struct fwnode_handle *parent) const struct fwnode_handle *parent)
......
...@@ -538,7 +538,9 @@ static int __init of_platform_default_populate_init(void) ...@@ -538,7 +538,9 @@ static int __init of_platform_default_populate_init(void)
} }
/* Populate everything else. */ /* Populate everything else. */
fw_devlink_pause();
of_platform_default_populate(NULL, NULL, NULL); of_platform_default_populate(NULL, NULL, NULL);
fw_devlink_resume();
return 0; return 0;
} }
......
// SPDX-License-Identifier: GPL-2.0 /* SPDX-License-Identifier: GPL-2.0 */
/* /*
* internal.h - declarations internal to debugfs * internal.h - declarations internal to debugfs
* *
......
...@@ -1010,7 +1010,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, ...@@ -1010,7 +1010,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
#ifdef CONFIG_DEBUG_LOCK_ALLOC #ifdef CONFIG_DEBUG_LOCK_ALLOC
if (key) { if (key) {
lockdep_init_map(&kn->dep_map, "kn->count", key, 0); lockdep_init_map(&kn->dep_map, "kn->active", key, 0);
kn->flags |= KERNFS_LOCKDEP; kn->flags |= KERNFS_LOCKDEP;
} }
#endif #endif
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
struct firmware { struct firmware {
size_t size; size_t size;
const u8 *data; const u8 *data;
struct page **pages;
/* firmware loader private fields */ /* firmware loader private fields */
void *priv; void *priv;
......
...@@ -171,5 +171,7 @@ struct fwnode_operations { ...@@ -171,5 +171,7 @@ struct fwnode_operations {
#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev) #define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev)
extern u32 fw_devlink_get_flags(void); extern u32 fw_devlink_get_flags(void);
void fw_devlink_pause(void);
void fw_devlink_resume(void);
#endif #endif
...@@ -444,6 +444,7 @@ int software_node_register_node_group(const struct software_node **node_group); ...@@ -444,6 +444,7 @@ int software_node_register_node_group(const struct software_node **node_group);
void software_node_unregister_node_group(const struct software_node **node_group); void software_node_unregister_node_group(const struct software_node **node_group);
int software_node_register(const struct software_node *node); int software_node_register(const struct software_node *node);
void software_node_unregister(const struct software_node *node);
int software_node_notify(struct device *dev, unsigned long action); int software_node_notify(struct device *dev, unsigned long action);
......
...@@ -620,6 +620,13 @@ void kobject_del(struct kobject *kobj) ...@@ -620,6 +620,13 @@ void kobject_del(struct kobject *kobj)
if (ktype) if (ktype)
sysfs_remove_groups(kobj, ktype->default_groups); sysfs_remove_groups(kobj, ktype->default_groups);
/* send "remove" if the caller did not do it but sent "add" */
if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) {
pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n",
kobject_name(kobj), kobj);
kobject_uevent(kobj, KOBJ_REMOVE);
}
sysfs_remove_dir(kobj); sysfs_remove_dir(kobj);
sysfs_put(sd); sysfs_put(sd);
...@@ -673,13 +680,6 @@ static void kobject_cleanup(struct kobject *kobj) ...@@ -673,13 +680,6 @@ static void kobject_cleanup(struct kobject *kobj)
pr_debug("kobject: '%s' (%p): does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n", pr_debug("kobject: '%s' (%p): does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n",
kobject_name(kobj), kobj); kobject_name(kobj), kobj);
/* send "remove" if the caller did not do it but sent "add" */
if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) {
pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n",
kobject_name(kobj), kobj);
kobject_uevent(kobj, KOBJ_REMOVE);
}
/* remove from sysfs if the caller did not do it */ /* remove from sysfs if the caller did not do it */
if (kobj->state_in_sysfs) { if (kobj->state_in_sysfs) {
pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n", pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n",
......
...@@ -310,27 +310,13 @@ static int test_dev_config_update_bool(const char *buf, size_t size, ...@@ -310,27 +310,13 @@ static int test_dev_config_update_bool(const char *buf, size_t size,
return ret; return ret;
} }
static ssize_t static ssize_t test_dev_config_show_bool(char *buf, bool val)
test_dev_config_show_bool(char *buf,
bool config)
{ {
bool val;
mutex_lock(&test_fw_mutex);
val = config;
mutex_unlock(&test_fw_mutex);
return snprintf(buf, PAGE_SIZE, "%d\n", val); return snprintf(buf, PAGE_SIZE, "%d\n", val);
} }
static ssize_t test_dev_config_show_int(char *buf, int cfg) static ssize_t test_dev_config_show_int(char *buf, int val)
{ {
int val;
mutex_lock(&test_fw_mutex);
val = cfg;
mutex_unlock(&test_fw_mutex);
return snprintf(buf, PAGE_SIZE, "%d\n", val); return snprintf(buf, PAGE_SIZE, "%d\n", val);
} }
...@@ -354,14 +340,8 @@ static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) ...@@ -354,14 +340,8 @@ static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
return size; return size;
} }
static ssize_t test_dev_config_show_u8(char *buf, u8 cfg) static ssize_t test_dev_config_show_u8(char *buf, u8 val)
{ {
u8 val;
mutex_lock(&test_fw_mutex);
val = cfg;
mutex_unlock(&test_fw_mutex);
return snprintf(buf, PAGE_SIZE, "%u\n", val); return snprintf(buf, PAGE_SIZE, "%u\n", val);
} }
......
...@@ -644,7 +644,9 @@ static void __init fwnode_pointer(void) ...@@ -644,7 +644,9 @@ static void __init fwnode_pointer(void)
test(second_name, "%pfwP", software_node_fwnode(&softnodes[1])); test(second_name, "%pfwP", software_node_fwnode(&softnodes[1]));
test(third_name, "%pfwP", software_node_fwnode(&softnodes[2])); test(third_name, "%pfwP", software_node_fwnode(&softnodes[2]));
software_node_unregister_nodes(softnodes); software_node_unregister(&softnodes[2]);
software_node_unregister(&softnodes[1]);
software_node_unregister(&softnodes[0]);
} }
static void __init static void __init
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment