forked from Minki/linux
Driver core changes for 5.13-rc1
Here is the "big" set of driver core changes for 5.13-rc1. Nothing major, just lots of little core changes and cleanups, notable things are: - finally set fw_devlink=on by default. All reported issues with this have been shaken out over the past 9 months or so, but we will be paying attention to any fallout here in case we need to revert this as the default boot value (symptoms of problems are a simple lack of booting) - fixes found to be needed by fw_devlink=on value in some subsystems (like clock). - delayed work initialization cleanup - driver core cleanups and minor updates - software node cleanups and tweaks - devtmpfs cleanups - minor debugfs cleanups All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYIazPA8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ylzUwCguQ+VUs1d0voq/oKiqR+lbXnQf3kAn0jf/eom ucRSdeIc21eEE83Ei9aZ =pchl -----END PGP SIGNATURE----- Merge tag 'driver-core-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core Pull driver core updates from Greg KH: "Here is the "big" set of driver core changes for 5.13-rc1. Nothing major, just lots of little core changes and cleanups, notable things are: - finally set 'fw_devlink=on' by default. All reported issues with this have been shaken out over the past 9 months or so, but we will be paying attention to any fallout here in case we need to revert this as the default boot value (symptoms of problems are a simple lack of booting) - fixes found to be needed by fw_devlink=on value in some subsystems (like clock). - delayed work initialization cleanup - driver core cleanups and minor updates - software node cleanups and tweaks - devtmpfs cleanups - minor debugfs cleanups All of these have been in linux-next for a while with no reported issues" * tag 'driver-core-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (53 commits) devm-helpers: Fix devm_delayed_work_autocancel() kerneldoc PM / wakeup: use dev_set_name() directly software node: Allow node addition to already existing device kunit: software node: adhear to KUNIT formatting standard node: fix device cleanups in error handling code kobject_uevent: remove warning in init_uevent_argv() debugfs: Make debugfs_allow RO after init Revert "driver core: platform: Make platform_get_irq_optional() optional" media: ipu3-cio2: Switch to use SOFTWARE_NODE_REFERENCE() software node: Introduce SOFTWARE_NODE_REFERENCE() helper macro software node: Imply kobj_to_swnode() to be no-op software node: Deduplicate code in fwnode_create_software_node() software node: Introduce software_node_alloc()/software_node_free() software node: Free resources explicitly when swnode_register() fails debugfs: drop pointless nul-termination in debugfs_read_file_bool() driver core: add helper for deferred probe reason setting driver core: Improve fw_devlink & deferred_probe_timeout interaction of: property: fw_devlink: Add support for remote-endpoint driver core: platform: Make platform_get_irq_optional() optional driver core: Replace printf() specifier and drop unneeded casting ...
This commit is contained in:
commit
c01c0716cc
@ -5197,6 +5197,12 @@ M: Torben Mathiasen <device@lanana.org>
|
||||
S: Maintained
|
||||
W: http://lanana.org/docs/device-list/index.html
|
||||
|
||||
DEVICE RESOURCE MANAGEMENT HELPERS
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
R: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
|
||||
S: Maintained
|
||||
F: include/linux/devm-helpers.h
|
||||
|
||||
DEVICE-MAPPER (LVM)
|
||||
M: Alasdair Kergon <agk@redhat.com>
|
||||
M: Mike Snitzer <snitzer@redhat.com>
|
||||
|
@ -461,6 +461,10 @@ attribute_container_add_class_device(struct device *classdev)
|
||||
/**
|
||||
* attribute_container_add_class_device_adapter - simple adapter for triggers
|
||||
*
|
||||
* @cont: the container to register.
|
||||
* @dev: the generic device to activate the trigger for
|
||||
* @classdev: the class device to add
|
||||
*
|
||||
* This function is identical to attribute_container_add_class_device except
|
||||
* that it is designed to be called from the triggers
|
||||
*/
|
||||
|
@ -265,8 +265,3 @@ void __init auxiliary_bus_init(void)
|
||||
{
|
||||
WARN_ON(bus_register(&auxiliary_bus_type));
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Auxiliary Bus");
|
||||
MODULE_AUTHOR("David Ertman <david.m.ertman@intel.com>");
|
||||
MODULE_AUTHOR("Kiran Patil <kiran.patil@intel.com>");
|
||||
|
@ -185,11 +185,13 @@ extern int device_links_read_lock(void);
|
||||
extern void device_links_read_unlock(int idx);
|
||||
extern int device_links_read_lock_held(void);
|
||||
extern int device_links_check_suppliers(struct device *dev);
|
||||
extern void device_links_force_bind(struct device *dev);
|
||||
extern void device_links_driver_bound(struct device *dev);
|
||||
extern void device_links_driver_cleanup(struct device *dev);
|
||||
extern void device_links_no_driver(struct device *dev);
|
||||
extern bool device_links_busy(struct device *dev);
|
||||
extern void device_links_unbind_consumers(struct device *dev);
|
||||
extern void fw_devlink_drivers_done(void);
|
||||
|
||||
/* device pm support */
|
||||
void device_pm_move_to_tail(struct device *dev);
|
||||
|
@ -65,7 +65,6 @@ struct master {
|
||||
const struct component_master_ops *ops;
|
||||
struct device *dev;
|
||||
struct component_match *match;
|
||||
struct dentry *dentry;
|
||||
};
|
||||
|
||||
struct component {
|
||||
@ -125,15 +124,13 @@ core_initcall(component_debug_init);
|
||||
|
||||
static void component_master_debugfs_add(struct master *m)
|
||||
{
|
||||
m->dentry = debugfs_create_file(dev_name(m->dev), 0444,
|
||||
component_debugfs_dir,
|
||||
m, &component_devices_fops);
|
||||
debugfs_create_file(dev_name(m->dev), 0444, component_debugfs_dir, m,
|
||||
&component_devices_fops);
|
||||
}
|
||||
|
||||
static void component_master_debugfs_del(struct master *m)
|
||||
{
|
||||
debugfs_remove(m->dentry);
|
||||
m->dentry = NULL;
|
||||
debugfs_remove(debugfs_lookup(dev_name(m->dev), component_debugfs_dir));
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -51,6 +51,7 @@ static LIST_HEAD(deferred_sync);
|
||||
static unsigned int defer_sync_state_count = 1;
|
||||
static DEFINE_MUTEX(fwnode_link_lock);
|
||||
static bool fw_devlink_is_permissive(void);
|
||||
static bool fw_devlink_drv_reg_done;
|
||||
|
||||
/**
|
||||
* fwnode_link_add - Create a link between two fwnode_handles.
|
||||
@ -1153,6 +1154,41 @@ static ssize_t waiting_for_supplier_show(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR_RO(waiting_for_supplier);
|
||||
|
||||
/**
|
||||
* device_links_force_bind - Prepares device to be force bound
|
||||
* @dev: Consumer device.
|
||||
*
|
||||
* device_bind_driver() force binds a device to a driver without calling any
|
||||
* driver probe functions. So the consumer really isn't going to wait for any
|
||||
* supplier before it's bound to the driver. We still want the device link
|
||||
* states to be sensible when this happens.
|
||||
*
|
||||
* In preparation for device_bind_driver(), this function goes through each
|
||||
* supplier device links and checks if the supplier is bound. If it is, then
|
||||
* the device link status is set to CONSUMER_PROBE. Otherwise, the device link
|
||||
* is dropped. Links without the DL_FLAG_MANAGED flag set are ignored.
|
||||
*/
|
||||
void device_links_force_bind(struct device *dev)
|
||||
{
|
||||
struct device_link *link, *ln;
|
||||
|
||||
device_links_write_lock();
|
||||
|
||||
list_for_each_entry_safe(link, ln, &dev->links.suppliers, c_node) {
|
||||
if (!(link->flags & DL_FLAG_MANAGED))
|
||||
continue;
|
||||
|
||||
if (link->status != DL_STATE_AVAILABLE) {
|
||||
device_link_drop_managed(link);
|
||||
continue;
|
||||
}
|
||||
WRITE_ONCE(link->status, DL_STATE_CONSUMER_PROBE);
|
||||
}
|
||||
dev->links.status = DL_DEV_PROBING;
|
||||
|
||||
device_links_write_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* device_links_driver_bound - Update device links after probing its driver.
|
||||
* @dev: Device to update the links for.
|
||||
@ -1503,7 +1539,7 @@ static void device_links_purge(struct device *dev)
|
||||
#define FW_DEVLINK_FLAGS_RPM (FW_DEVLINK_FLAGS_ON | \
|
||||
DL_FLAG_PM_RUNTIME)
|
||||
|
||||
static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
|
||||
static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_ON;
|
||||
static int __init fw_devlink_setup(char *arg)
|
||||
{
|
||||
if (!arg)
|
||||
@ -1563,6 +1599,52 @@ static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode)
|
||||
fw_devlink_parse_fwtree(child);
|
||||
}
|
||||
|
||||
static void fw_devlink_relax_link(struct device_link *link)
|
||||
{
|
||||
if (!(link->flags & DL_FLAG_INFERRED))
|
||||
return;
|
||||
|
||||
if (link->flags == (DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE))
|
||||
return;
|
||||
|
||||
pm_runtime_drop_link(link);
|
||||
link->flags = DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE;
|
||||
dev_dbg(link->consumer, "Relaxing link with %s\n",
|
||||
dev_name(link->supplier));
|
||||
}
|
||||
|
||||
static int fw_devlink_no_driver(struct device *dev, void *data)
|
||||
{
|
||||
struct device_link *link = to_devlink(dev);
|
||||
|
||||
if (!link->supplier->can_match)
|
||||
fw_devlink_relax_link(link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fw_devlink_drivers_done(void)
|
||||
{
|
||||
fw_devlink_drv_reg_done = true;
|
||||
device_links_write_lock();
|
||||
class_for_each_device(&devlink_class, NULL, NULL,
|
||||
fw_devlink_no_driver);
|
||||
device_links_write_unlock();
|
||||
}
|
||||
|
||||
static void fw_devlink_unblock_consumers(struct device *dev)
|
||||
{
|
||||
struct device_link *link;
|
||||
|
||||
if (!fw_devlink_flags || fw_devlink_is_permissive())
|
||||
return;
|
||||
|
||||
device_links_write_lock();
|
||||
list_for_each_entry(link, &dev->links.consumers, s_node)
|
||||
fw_devlink_relax_link(link);
|
||||
device_links_write_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* fw_devlink_relax_cycle - Convert cyclic links to SYNC_STATE_ONLY links
|
||||
* @con: Device to check dependencies for.
|
||||
@ -1599,21 +1681,16 @@ static int fw_devlink_relax_cycle(struct device *con, void *sup)
|
||||
|
||||
ret = 1;
|
||||
|
||||
if (!(link->flags & DL_FLAG_INFERRED))
|
||||
continue;
|
||||
|
||||
pm_runtime_drop_link(link);
|
||||
link->flags = DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE;
|
||||
dev_dbg(link->consumer, "Relaxing link with %s\n",
|
||||
dev_name(link->supplier));
|
||||
fw_devlink_relax_link(link);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @con: consumer device for the device link
|
||||
* @sup_handle: fwnode handle of supplier
|
||||
* @flags: devlink flags
|
||||
*
|
||||
* This function will try to create a device link between the consumer device
|
||||
* @con and the supplier device represented by @sup_handle.
|
||||
@ -1709,7 +1786,7 @@ out:
|
||||
|
||||
/**
|
||||
* __fw_devlink_link_to_consumers - Create device links to consumers of a device
|
||||
* @dev - Device that needs to be linked to its consumers
|
||||
* @dev: Device that needs to be linked to its consumers
|
||||
*
|
||||
* This function looks at all the consumer fwnodes of @dev and creates device
|
||||
* links between the consumer device and @dev (supplier).
|
||||
@ -1779,8 +1856,8 @@ static void __fw_devlink_link_to_consumers(struct device *dev)
|
||||
|
||||
/**
|
||||
* __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
|
||||
* @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 looks at all the supplier fwnodes of fwnode tree rooted at
|
||||
* @fwnode and creates device links between @dev (consumer) and all the
|
||||
@ -3240,6 +3317,15 @@ int device_add(struct device *dev)
|
||||
}
|
||||
|
||||
bus_probe_device(dev);
|
||||
|
||||
/*
|
||||
* If all driver registration is done and a newly added device doesn't
|
||||
* match with any driver, don't block its consumers from probing in
|
||||
* case the consumer device is able to operate without this supplier.
|
||||
*/
|
||||
if (dev->fwnode && fw_devlink_drv_reg_done && !dev->can_match)
|
||||
fw_devlink_unblock_consumers(dev);
|
||||
|
||||
if (parent)
|
||||
klist_add_tail(&dev->p->knode_parent,
|
||||
&parent->p->klist_children);
|
||||
|
@ -409,13 +409,11 @@ __cpu_device_create(struct device *parent, void *drvdata,
|
||||
const char *fmt, va_list args)
|
||||
{
|
||||
struct device *dev = NULL;
|
||||
int retval = -ENODEV;
|
||||
int retval = -ENOMEM;
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
retval = -ENOMEM;
|
||||
if (!dev)
|
||||
goto error;
|
||||
}
|
||||
|
||||
device_initialize(dev);
|
||||
dev->parent = parent;
|
||||
|
@ -55,7 +55,6 @@ static DEFINE_MUTEX(deferred_probe_mutex);
|
||||
static LIST_HEAD(deferred_probe_pending_list);
|
||||
static LIST_HEAD(deferred_probe_active_list);
|
||||
static atomic_t deferred_trigger_count = ATOMIC_INIT(0);
|
||||
static struct dentry *deferred_devices;
|
||||
static bool initcalls_done;
|
||||
|
||||
/* Save the async probe drivers' name from kernel cmdline */
|
||||
@ -69,6 +68,12 @@ static char async_probe_drv_names[ASYNC_DRV_NAMES_MAX_LEN];
|
||||
*/
|
||||
static bool defer_all_probes;
|
||||
|
||||
static void __device_set_deferred_probe_reason(const struct device *dev, char *reason)
|
||||
{
|
||||
kfree(dev->p->deferred_probe_reason);
|
||||
dev->p->deferred_probe_reason = reason;
|
||||
}
|
||||
|
||||
/*
|
||||
* deferred_probe_work_func() - Retry probing devices in the active list.
|
||||
*/
|
||||
@ -97,8 +102,7 @@ static void deferred_probe_work_func(struct work_struct *work)
|
||||
|
||||
get_device(dev);
|
||||
|
||||
kfree(dev->p->deferred_probe_reason);
|
||||
dev->p->deferred_probe_reason = NULL;
|
||||
__device_set_deferred_probe_reason(dev, NULL);
|
||||
|
||||
/*
|
||||
* Drop the mutex while probing each device; the probe path may
|
||||
@ -126,6 +130,9 @@ static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func);
|
||||
|
||||
void driver_deferred_probe_add(struct device *dev)
|
||||
{
|
||||
if (!dev->can_match)
|
||||
return;
|
||||
|
||||
mutex_lock(&deferred_probe_mutex);
|
||||
if (list_empty(&dev->p->deferred_probe)) {
|
||||
dev_dbg(dev, "Added to deferred list\n");
|
||||
@ -140,8 +147,7 @@ void driver_deferred_probe_del(struct device *dev)
|
||||
if (!list_empty(&dev->p->deferred_probe)) {
|
||||
dev_dbg(dev, "Removed from deferred list\n");
|
||||
list_del_init(&dev->p->deferred_probe);
|
||||
kfree(dev->p->deferred_probe_reason);
|
||||
dev->p->deferred_probe_reason = NULL;
|
||||
__device_set_deferred_probe_reason(dev, NULL);
|
||||
}
|
||||
mutex_unlock(&deferred_probe_mutex);
|
||||
}
|
||||
@ -185,7 +191,7 @@ static void driver_deferred_probe_trigger(void)
|
||||
* Kick the re-probe thread. It may already be scheduled, but it is
|
||||
* safe to kick it again.
|
||||
*/
|
||||
schedule_work(&deferred_probe_work);
|
||||
queue_work(system_unbound_wq, &deferred_probe_work);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,11 +226,12 @@ void device_unblock_probing(void)
|
||||
void device_set_deferred_probe_reason(const struct device *dev, struct va_format *vaf)
|
||||
{
|
||||
const char *drv = dev_driver_string(dev);
|
||||
char *reason;
|
||||
|
||||
mutex_lock(&deferred_probe_mutex);
|
||||
|
||||
kfree(dev->p->deferred_probe_reason);
|
||||
dev->p->deferred_probe_reason = kasprintf(GFP_KERNEL, "%s: %pV", drv, vaf);
|
||||
reason = kasprintf(GFP_KERNEL, "%s: %pV", drv, vaf);
|
||||
__device_set_deferred_probe_reason(dev, reason);
|
||||
|
||||
mutex_unlock(&deferred_probe_mutex);
|
||||
}
|
||||
@ -294,6 +301,8 @@ static void deferred_probe_timeout_work_func(struct work_struct *work)
|
||||
{
|
||||
struct device_private *p;
|
||||
|
||||
fw_devlink_drivers_done();
|
||||
|
||||
driver_deferred_probe_timeout = 0;
|
||||
driver_deferred_probe_trigger();
|
||||
flush_work(&deferred_probe_work);
|
||||
@ -315,8 +324,8 @@ static DECLARE_DELAYED_WORK(deferred_probe_timeout_work, deferred_probe_timeout_
|
||||
*/
|
||||
static int deferred_probe_initcall(void)
|
||||
{
|
||||
deferred_devices = debugfs_create_file("devices_deferred", 0444, NULL,
|
||||
NULL, &deferred_devs_fops);
|
||||
debugfs_create_file("devices_deferred", 0444, NULL, NULL,
|
||||
&deferred_devs_fops);
|
||||
|
||||
driver_deferred_probe_enable = true;
|
||||
driver_deferred_probe_trigger();
|
||||
@ -324,6 +333,9 @@ static int deferred_probe_initcall(void)
|
||||
flush_work(&deferred_probe_work);
|
||||
initcalls_done = true;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_MODULES))
|
||||
fw_devlink_drivers_done();
|
||||
|
||||
/*
|
||||
* Trigger deferred probe again, this time we won't defer anything
|
||||
* that is optional
|
||||
@ -341,7 +353,7 @@ late_initcall(deferred_probe_initcall);
|
||||
|
||||
static void __exit deferred_probe_exit(void)
|
||||
{
|
||||
debugfs_remove_recursive(deferred_devices);
|
||||
debugfs_remove_recursive(debugfs_lookup("devices_deferred", NULL));
|
||||
}
|
||||
__exitcall(deferred_probe_exit);
|
||||
|
||||
@ -418,8 +430,11 @@ static int driver_sysfs_add(struct device *dev)
|
||||
if (ret)
|
||||
goto rm_dev;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump ||
|
||||
!device_create_file(dev, &dev_attr_coredump))
|
||||
if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump)
|
||||
return 0;
|
||||
|
||||
ret = device_create_file(dev, &dev_attr_coredump);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
sysfs_remove_link(&dev->kobj, "driver");
|
||||
@ -462,8 +477,10 @@ int device_bind_driver(struct device *dev)
|
||||
int ret;
|
||||
|
||||
ret = driver_sysfs_add(dev);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
device_links_force_bind(dev);
|
||||
driver_bound(dev);
|
||||
}
|
||||
else if (dev->bus)
|
||||
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
|
||||
BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
|
||||
@ -731,6 +748,7 @@ static int driver_probe_device(struct device_driver *drv, struct device *dev)
|
||||
if (!device_is_registered(dev))
|
||||
return -ENODEV;
|
||||
|
||||
dev->can_match = true;
|
||||
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
|
||||
drv->bus->name, __func__, dev_name(dev), drv->name);
|
||||
|
||||
@ -834,6 +852,7 @@ static int __device_attach_driver(struct device_driver *drv, void *_data)
|
||||
return 0;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
dev_dbg(dev, "Device match requests probe deferral\n");
|
||||
dev->can_match = true;
|
||||
driver_deferred_probe_add(dev);
|
||||
} else if (ret < 0) {
|
||||
dev_dbg(dev, "Bus failed to match device: %d\n", ret);
|
||||
@ -1069,6 +1088,7 @@ static int __driver_attach(struct device *dev, void *data)
|
||||
return 0;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
dev_dbg(dev, "Device match requests probe deferral\n");
|
||||
dev->can_match = true;
|
||||
driver_deferred_probe_add(dev);
|
||||
} else if (ret < 0) {
|
||||
dev_dbg(dev, "Bus failed to match device: %d\n", ret);
|
||||
|
@ -202,7 +202,7 @@ static int devcd_match_failing(struct device *dev, const void *failing)
|
||||
* NOTE: if two tables allocated with devcd_alloc_sgtable and then chained
|
||||
* using the sg_chain function then that function should be called only once
|
||||
* on the chained table
|
||||
* @table: pointer to sg_table to free
|
||||
* @data: pointer to sg_table to free
|
||||
*/
|
||||
static void devcd_free_sgtable(void *data)
|
||||
{
|
||||
@ -210,7 +210,7 @@ static void devcd_free_sgtable(void *data)
|
||||
}
|
||||
|
||||
/**
|
||||
* devcd_read_from_table - copy data from sg_table to a given buffer
|
||||
* devcd_read_from_sgtable - copy data from sg_table to a given buffer
|
||||
* and return the number of bytes read
|
||||
* @buffer: the buffer to copy the data to it
|
||||
* @buf_len: the length of the buffer
|
||||
@ -292,13 +292,16 @@ void dev_coredumpm(struct device *dev, struct module *owner,
|
||||
if (device_add(&devcd->devcd_dev))
|
||||
goto put_device;
|
||||
|
||||
/*
|
||||
* These should normally not fail, but there is no problem
|
||||
* continuing without the links, so just warn instead of
|
||||
* failing.
|
||||
*/
|
||||
if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj,
|
||||
"failing_device"))
|
||||
/* nothing - symlink will be missing */;
|
||||
|
||||
if (sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj,
|
||||
"devcoredump"))
|
||||
/* nothing - symlink will be missing */;
|
||||
"failing_device") ||
|
||||
sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj,
|
||||
"devcoredump"))
|
||||
dev_warn(dev, "devcoredump create_link failed\n");
|
||||
|
||||
INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
|
||||
schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT);
|
||||
|
@ -58,8 +58,8 @@ static void devres_log(struct device *dev, struct devres_node *node,
|
||||
const char *op)
|
||||
{
|
||||
if (unlikely(log_devres))
|
||||
dev_err(dev, "DEVRES %3s %p %s (%lu bytes)\n",
|
||||
op, node, node->name, (unsigned long)node->size);
|
||||
dev_err(dev, "DEVRES %3s %p %s (%zu bytes)\n",
|
||||
op, node, node->name, node->size);
|
||||
}
|
||||
#else /* CONFIG_DEBUG_DEVRES */
|
||||
#define set_node_dbginfo(node, n, s) do {} while (0)
|
||||
@ -1228,6 +1228,6 @@ EXPORT_SYMBOL_GPL(__devm_alloc_percpu);
|
||||
void devm_free_percpu(struct device *dev, void __percpu *pdata)
|
||||
{
|
||||
WARN_ON(devres_destroy(dev, devm_percpu_release, devm_percpu_match,
|
||||
(void *)pdata));
|
||||
(__force void *)pdata));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_free_percpu);
|
||||
|
@ -371,7 +371,7 @@ int __init devtmpfs_mount(void)
|
||||
return err;
|
||||
}
|
||||
|
||||
static DECLARE_COMPLETION(setup_done);
|
||||
static __initdata DECLARE_COMPLETION(setup_done);
|
||||
|
||||
static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid,
|
||||
struct device *dev)
|
||||
@ -405,7 +405,7 @@ static void __noreturn devtmpfs_work_loop(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int __init devtmpfs_setup(void *p)
|
||||
static noinline int __init devtmpfs_setup(void *p)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -419,7 +419,6 @@ static int __init devtmpfs_setup(void *p)
|
||||
init_chroot(".");
|
||||
out:
|
||||
*(int *)p = err;
|
||||
complete(&setup_done);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -432,6 +431,7 @@ static int __ref devtmpfsd(void *p)
|
||||
{
|
||||
int err = devtmpfs_setup(p);
|
||||
|
||||
complete(&setup_done);
|
||||
if (err)
|
||||
return err;
|
||||
devtmpfs_work_loop();
|
||||
|
@ -268,21 +268,20 @@ static void node_init_cache_dev(struct node *node)
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
device_initialize(dev);
|
||||
dev->parent = &node->dev;
|
||||
dev->release = node_cache_release;
|
||||
if (dev_set_name(dev, "memory_side_cache"))
|
||||
goto free_dev;
|
||||
goto put_device;
|
||||
|
||||
if (device_register(dev))
|
||||
goto free_name;
|
||||
if (device_add(dev))
|
||||
goto put_device;
|
||||
|
||||
pm_runtime_no_callbacks(dev);
|
||||
node->cache_dev = dev;
|
||||
return;
|
||||
free_name:
|
||||
kfree_const(dev->kobj.name);
|
||||
free_dev:
|
||||
kfree(dev);
|
||||
put_device:
|
||||
put_device(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,25 +318,24 @@ void node_add_cache(unsigned int nid, struct node_cache_attrs *cache_attrs)
|
||||
return;
|
||||
|
||||
dev = &info->dev;
|
||||
device_initialize(dev);
|
||||
dev->parent = node->cache_dev;
|
||||
dev->release = node_cacheinfo_release;
|
||||
dev->groups = cache_groups;
|
||||
if (dev_set_name(dev, "index%d", cache_attrs->level))
|
||||
goto free_cache;
|
||||
goto put_device;
|
||||
|
||||
info->cache_attrs = *cache_attrs;
|
||||
if (device_register(dev)) {
|
||||
if (device_add(dev)) {
|
||||
dev_warn(&node->dev, "failed to add cache level:%d\n",
|
||||
cache_attrs->level);
|
||||
goto free_name;
|
||||
goto put_device;
|
||||
}
|
||||
pm_runtime_no_callbacks(dev);
|
||||
list_add_tail(&info->node, &node->cache_attrs);
|
||||
return;
|
||||
free_name:
|
||||
kfree_const(dev->kobj.name);
|
||||
free_cache:
|
||||
kfree(info);
|
||||
put_device:
|
||||
put_device(dev);
|
||||
}
|
||||
|
||||
static void node_remove_caches(struct node *node)
|
||||
|
@ -316,10 +316,11 @@ void *platform_msi_get_host_data(struct irq_domain *domain)
|
||||
}
|
||||
|
||||
/**
|
||||
* platform_msi_create_device_domain - Create a platform-msi domain
|
||||
* __platform_msi_create_device_domain - Create a platform-msi domain
|
||||
*
|
||||
* @dev: The device generating the MSIs
|
||||
* @nvec: The number of MSIs that need to be allocated
|
||||
* @is_tree: flag to indicate tree hierarchy
|
||||
* @write_msi_msg: Callback to write an interrupt message for @dev
|
||||
* @ops: The hierarchy domain operations to use
|
||||
* @host_data: Private data associated to this domain
|
||||
|
@ -192,7 +192,7 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
|
||||
#ifdef CONFIG_SPARC
|
||||
/* sparc does not have irqs represented as IORESOURCE_IRQ resources */
|
||||
if (!dev || num >= dev->archdata.num_irqs)
|
||||
return -ENXIO;
|
||||
goto out_not_found;
|
||||
ret = dev->archdata.irqs[num];
|
||||
goto out;
|
||||
#else
|
||||
@ -223,10 +223,8 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
|
||||
struct irq_data *irqd;
|
||||
|
||||
irqd = irq_get_irq_data(r->start);
|
||||
if (!irqd) {
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
if (!irqd)
|
||||
goto out_not_found;
|
||||
irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS);
|
||||
}
|
||||
|
||||
@ -249,8 +247,9 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = -ENXIO;
|
||||
#endif
|
||||
out_not_found:
|
||||
ret = -ENXIO;
|
||||
out:
|
||||
WARN(ret == 0, "0 is an invalid IRQ number\n");
|
||||
return ret;
|
||||
|
@ -154,7 +154,7 @@ static struct device *wakeup_source_device_create(struct device *parent,
|
||||
dev_set_drvdata(dev, ws);
|
||||
device_set_pm_not_required(dev);
|
||||
|
||||
retval = kobject_set_name(&dev->kobj, "wakeup%d", ws->id);
|
||||
retval = dev_set_name(dev, "wakeup%d", ws->id);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
|
@ -12,10 +12,10 @@
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct swnode {
|
||||
int id;
|
||||
struct kobject kobj;
|
||||
struct fwnode_handle fwnode;
|
||||
const struct software_node *node;
|
||||
int id;
|
||||
|
||||
/* hierarchy */
|
||||
struct ida child_ids;
|
||||
@ -720,19 +720,30 @@ software_node_find_by_name(const struct software_node *parent, const char *name)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(software_node_find_by_name);
|
||||
|
||||
static int
|
||||
software_node_register_properties(struct software_node *node,
|
||||
const struct property_entry *properties)
|
||||
static struct software_node *software_node_alloc(const struct property_entry *properties)
|
||||
{
|
||||
struct property_entry *props;
|
||||
struct software_node *node;
|
||||
|
||||
props = property_entries_dup(properties);
|
||||
if (IS_ERR(props))
|
||||
return PTR_ERR(props);
|
||||
return ERR_CAST(props);
|
||||
|
||||
node = kzalloc(sizeof(*node), GFP_KERNEL);
|
||||
if (!node) {
|
||||
property_entries_free(props);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
node->properties = props;
|
||||
|
||||
return 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
static void software_node_free(const struct software_node *node)
|
||||
{
|
||||
property_entries_free(node->properties);
|
||||
kfree(node);
|
||||
}
|
||||
|
||||
static void software_node_release(struct kobject *kobj)
|
||||
@ -746,10 +757,9 @@ static void software_node_release(struct kobject *kobj)
|
||||
ida_simple_remove(&swnode_root_ids, swnode->id);
|
||||
}
|
||||
|
||||
if (swnode->allocated) {
|
||||
property_entries_free(swnode->node->properties);
|
||||
kfree(swnode->node);
|
||||
}
|
||||
if (swnode->allocated)
|
||||
software_node_free(swnode->node);
|
||||
|
||||
ida_destroy(&swnode->child_ids);
|
||||
kfree(swnode);
|
||||
}
|
||||
@ -767,22 +777,19 @@ swnode_register(const struct software_node *node, struct swnode *parent,
|
||||
int ret;
|
||||
|
||||
swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
|
||||
if (!swnode) {
|
||||
ret = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
if (!swnode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids,
|
||||
0, 0, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
kfree(swnode);
|
||||
goto out_err;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
swnode->id = ret;
|
||||
swnode->node = node;
|
||||
swnode->parent = parent;
|
||||
swnode->allocated = allocated;
|
||||
swnode->kobj.kset = swnode_kset;
|
||||
fwnode_init(&swnode->fwnode, &software_node_ops);
|
||||
|
||||
@ -803,16 +810,17 @@ swnode_register(const struct software_node *node, struct swnode *parent,
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign the flag only in the successful case, so
|
||||
* the above kobject_put() won't mess up with properties.
|
||||
*/
|
||||
swnode->allocated = allocated;
|
||||
|
||||
if (parent)
|
||||
list_add_tail(&swnode->entry, &parent->children);
|
||||
|
||||
kobject_uevent(&swnode->kobj, KOBJ_ADD);
|
||||
return &swnode->fwnode;
|
||||
|
||||
out_err:
|
||||
if (allocated)
|
||||
property_entries_free(node->properties);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -880,7 +888,11 @@ EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
|
||||
* software_node_register_node_group - Register a group of software nodes
|
||||
* @node_group: NULL terminated array of software node pointers to be registered
|
||||
*
|
||||
* Register multiple software nodes at once.
|
||||
* Register multiple software nodes at once. If any node in the array
|
||||
* has its .parent pointer set (which can only be to another software_node),
|
||||
* then its parent **must** have been registered before it is; either outside
|
||||
* of this function or by ordering the array such that parent comes before
|
||||
* child.
|
||||
*/
|
||||
int software_node_register_node_group(const struct software_node **node_group)
|
||||
{
|
||||
@ -906,10 +918,14 @@ EXPORT_SYMBOL_GPL(software_node_register_node_group);
|
||||
* software_node_unregister_node_group - Unregister a group of software nodes
|
||||
* @node_group: NULL terminated array of software node pointers to be unregistered
|
||||
*
|
||||
* Unregister multiple software nodes at once. The array will be unwound in
|
||||
* reverse order (i.e. last entry first) and thus if any members of the array are
|
||||
* children of another member then the children must appear later in the list such
|
||||
* that they are unregistered first.
|
||||
* Unregister multiple software nodes at once. If parent pointers are set up
|
||||
* in any of the software nodes then the array **must** be ordered such that
|
||||
* parents come before their children.
|
||||
*
|
||||
* NOTE: If you are uncertain whether the array is ordered such that
|
||||
* parents will be unregistered before their children, it is wiser to
|
||||
* remove the nodes individually, in the correct order (child before
|
||||
* parent).
|
||||
*/
|
||||
void software_node_unregister_node_group(
|
||||
const struct software_node **node_group)
|
||||
@ -963,31 +979,28 @@ struct fwnode_handle *
|
||||
fwnode_create_software_node(const struct property_entry *properties,
|
||||
const struct fwnode_handle *parent)
|
||||
{
|
||||
struct fwnode_handle *fwnode;
|
||||
struct software_node *node;
|
||||
struct swnode *p = NULL;
|
||||
int ret;
|
||||
struct swnode *p;
|
||||
|
||||
if (parent) {
|
||||
if (IS_ERR(parent))
|
||||
return ERR_CAST(parent);
|
||||
if (!is_software_node(parent))
|
||||
return ERR_PTR(-EINVAL);
|
||||
p = to_swnode(parent);
|
||||
}
|
||||
if (IS_ERR(parent))
|
||||
return ERR_CAST(parent);
|
||||
|
||||
node = kzalloc(sizeof(*node), GFP_KERNEL);
|
||||
if (!node)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
p = to_swnode(parent);
|
||||
if (parent && !p)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ret = software_node_register_properties(node, properties);
|
||||
if (ret) {
|
||||
kfree(node);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
node = software_node_alloc(properties);
|
||||
if (IS_ERR(node))
|
||||
return ERR_CAST(node);
|
||||
|
||||
node->parent = p ? p->node : NULL;
|
||||
|
||||
return swnode_register(node, p, 1);
|
||||
fwnode = swnode_register(node, p, 1);
|
||||
if (IS_ERR(fwnode))
|
||||
software_node_free(node);
|
||||
|
||||
return fwnode;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_create_software_node);
|
||||
|
||||
@ -1032,6 +1045,7 @@ int device_add_software_node(struct device *dev, const struct software_node *nod
|
||||
}
|
||||
|
||||
set_secondary_fwnode(dev, &swnode->fwnode);
|
||||
software_node_notify(dev, KOBJ_ADD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1105,8 +1119,8 @@ int software_node_notify(struct device *dev, unsigned long action)
|
||||
|
||||
switch (action) {
|
||||
case KOBJ_ADD:
|
||||
ret = sysfs_create_link(&dev->kobj, &swnode->kobj,
|
||||
"software_node");
|
||||
ret = sysfs_create_link_nowarn(&dev->kobj, &swnode->kobj,
|
||||
"software_node");
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
|
@ -8,7 +8,7 @@ config TEST_ASYNC_DRIVER_PROBE
|
||||
The module name will be test_async_driver_probe.ko
|
||||
|
||||
If unsure say N.
|
||||
config KUNIT_DRIVER_PE_TEST
|
||||
config DRIVER_PE_KUNIT_TEST
|
||||
bool "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
|
||||
depends on KUNIT=y
|
||||
default KUNIT_ALL_TESTS
|
||||
|
@ -1,5 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o
|
||||
|
||||
obj-$(CONFIG_KUNIT_DRIVER_PE_TEST) += property-entry-test.o
|
||||
obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o
|
||||
CFLAGS_REMOVE_property-entry-test.o += -fplugin-arg-structleak_plugin-byref -fplugin-arg-structleak_plugin-byref-all
|
||||
|
@ -27,6 +27,9 @@ static void pe_test_uints(struct kunit *test)
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
error = fwnode_property_count_u8(node, "prop-u8");
|
||||
KUNIT_EXPECT_EQ(test, error, 1);
|
||||
|
||||
error = fwnode_property_read_u8(node, "prop-u8", &val_u8);
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)val_u8, 8);
|
||||
@ -48,6 +51,9 @@ static void pe_test_uints(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)val_u16, 16);
|
||||
|
||||
error = fwnode_property_count_u16(node, "prop-u16");
|
||||
KUNIT_EXPECT_EQ(test, error, 1);
|
||||
|
||||
error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1);
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)array_u16[0], 16);
|
||||
@ -65,6 +71,9 @@ static void pe_test_uints(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)val_u32, 32);
|
||||
|
||||
error = fwnode_property_count_u32(node, "prop-u32");
|
||||
KUNIT_EXPECT_EQ(test, error, 1);
|
||||
|
||||
error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1);
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)array_u32[0], 32);
|
||||
@ -82,6 +91,9 @@ static void pe_test_uints(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)val_u64, 64);
|
||||
|
||||
error = fwnode_property_count_u64(node, "prop-u64");
|
||||
KUNIT_EXPECT_EQ(test, error, 1);
|
||||
|
||||
error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1);
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)array_u64[0], 64);
|
||||
@ -95,15 +107,19 @@ static void pe_test_uints(struct kunit *test)
|
||||
error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1);
|
||||
KUNIT_EXPECT_NE(test, error, 0);
|
||||
|
||||
/* Count 64-bit values as 16-bit */
|
||||
error = fwnode_property_count_u16(node, "prop-u64");
|
||||
KUNIT_EXPECT_EQ(test, error, 4);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
static void pe_test_uint_arrays(struct kunit *test)
|
||||
{
|
||||
static const u8 a_u8[16] = { 8, 9 };
|
||||
static const u16 a_u16[16] = { 16, 17 };
|
||||
static const u32 a_u32[16] = { 32, 33 };
|
||||
static const u64 a_u64[16] = { 64, 65 };
|
||||
static const u8 a_u8[10] = { 8, 9 };
|
||||
static const u16 a_u16[10] = { 16, 17 };
|
||||
static const u32 a_u32[10] = { 32, 33 };
|
||||
static const u64 a_u64[10] = { 64, 65 };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U8_ARRAY("prop-u8", a_u8),
|
||||
PROPERTY_ENTRY_U16_ARRAY("prop-u16", a_u16),
|
||||
@ -126,6 +142,9 @@ static void pe_test_uint_arrays(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)val_u8, 8);
|
||||
|
||||
error = fwnode_property_count_u8(node, "prop-u8");
|
||||
KUNIT_EXPECT_EQ(test, error, 10);
|
||||
|
||||
error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 1);
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)array_u8[0], 8);
|
||||
@ -148,6 +167,9 @@ static void pe_test_uint_arrays(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)val_u16, 16);
|
||||
|
||||
error = fwnode_property_count_u16(node, "prop-u16");
|
||||
KUNIT_EXPECT_EQ(test, error, 10);
|
||||
|
||||
error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1);
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)array_u16[0], 16);
|
||||
@ -170,6 +192,9 @@ static void pe_test_uint_arrays(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)val_u32, 32);
|
||||
|
||||
error = fwnode_property_count_u32(node, "prop-u32");
|
||||
KUNIT_EXPECT_EQ(test, error, 10);
|
||||
|
||||
error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1);
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)array_u32[0], 32);
|
||||
@ -192,6 +217,9 @@ static void pe_test_uint_arrays(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)val_u64, 64);
|
||||
|
||||
error = fwnode_property_count_u64(node, "prop-u64");
|
||||
KUNIT_EXPECT_EQ(test, error, 10);
|
||||
|
||||
error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1);
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_EQ(test, (int)array_u64[0], 64);
|
||||
@ -210,6 +238,14 @@ static void pe_test_uint_arrays(struct kunit *test)
|
||||
error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1);
|
||||
KUNIT_EXPECT_NE(test, error, 0);
|
||||
|
||||
/* Count 64-bit values as 16-bit */
|
||||
error = fwnode_property_count_u16(node, "prop-u64");
|
||||
KUNIT_EXPECT_EQ(test, error, 40);
|
||||
|
||||
/* Other way around */
|
||||
error = fwnode_property_count_u64(node, "prop-u16");
|
||||
KUNIT_EXPECT_EQ(test, error, 2);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
@ -239,6 +275,9 @@ static void pe_test_strings(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_STREQ(test, str, "single");
|
||||
|
||||
error = fwnode_property_string_array_count(node, "str");
|
||||
KUNIT_EXPECT_EQ(test, error, 1);
|
||||
|
||||
error = fwnode_property_read_string_array(node, "str", strs, 1);
|
||||
KUNIT_EXPECT_EQ(test, error, 1);
|
||||
KUNIT_EXPECT_STREQ(test, strs[0], "single");
|
||||
@ -258,6 +297,9 @@ static void pe_test_strings(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, error, 0);
|
||||
KUNIT_EXPECT_STREQ(test, str, "");
|
||||
|
||||
error = fwnode_property_string_array_count(node, "strs");
|
||||
KUNIT_EXPECT_EQ(test, error, 2);
|
||||
|
||||
error = fwnode_property_read_string_array(node, "strs", strs, 3);
|
||||
KUNIT_EXPECT_EQ(test, error, 2);
|
||||
KUNIT_EXPECT_STREQ(test, strs[0], "string-a");
|
||||
@ -370,15 +412,8 @@ static void pe_test_reference(struct kunit *test)
|
||||
};
|
||||
|
||||
static const struct software_node_ref_args refs[] = {
|
||||
{
|
||||
.node = &nodes[0],
|
||||
.nargs = 0,
|
||||
},
|
||||
{
|
||||
.node = &nodes[1],
|
||||
.nargs = 2,
|
||||
.args = { 3, 4 },
|
||||
},
|
||||
SOFTWARE_NODE_REFERENCE(&nodes[0]),
|
||||
SOFTWARE_NODE_REFERENCE(&nodes[1], 3, 4),
|
||||
};
|
||||
|
||||
const struct property_entry entries[] = {
|
||||
|
@ -4610,6 +4610,8 @@ int of_clk_add_hw_provider(struct device_node *np,
|
||||
if (ret < 0)
|
||||
of_clk_del_provider(np);
|
||||
|
||||
fwnode_dev_initialized(&np->fwnode, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_clk_add_hw_provider);
|
||||
|
@ -9,6 +9,7 @@
|
||||
* (originally switch class is supported)
|
||||
*/
|
||||
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/extcon-provider.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/init.h>
|
||||
@ -112,7 +113,9 @@ static int gpio_extcon_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
INIT_DELAYED_WORK(&data->work, gpio_extcon_work);
|
||||
ret = devm_delayed_work_autocancel(dev, &data->work, gpio_extcon_work);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Request the interrupt of gpio to detect whether external connector
|
||||
@ -131,15 +134,6 @@ static int gpio_extcon_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_extcon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_extcon_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&data->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int gpio_extcon_resume(struct device *dev)
|
||||
{
|
||||
@ -158,7 +152,6 @@ static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume);
|
||||
|
||||
static struct platform_driver gpio_extcon_driver = {
|
||||
.probe = gpio_extcon_probe,
|
||||
.remove = gpio_extcon_remove,
|
||||
.driver = {
|
||||
.name = "extcon-gpio",
|
||||
.pm = &gpio_extcon_pm_ops,
|
||||
|
@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/extcon-provider.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -101,7 +102,9 @@ static int int3496_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
data->dev = dev;
|
||||
INIT_DELAYED_WORK(&data->work, int3496_do_usb_id);
|
||||
ret = devm_delayed_work_autocancel(dev, &data->work, int3496_do_usb_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->gpio_usb_id = devm_gpiod_get(dev, "id", GPIOD_IN);
|
||||
if (IS_ERR(data->gpio_usb_id)) {
|
||||
@ -155,16 +158,6 @@ static int int3496_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3496_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct int3496_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
devm_free_irq(&pdev->dev, data->usb_id_irq, data);
|
||||
cancel_delayed_work_sync(&data->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id int3496_acpi_match[] = {
|
||||
{ "INT3496" },
|
||||
{ }
|
||||
@ -177,7 +170,6 @@ static struct platform_driver int3496_driver = {
|
||||
.acpi_match_table = int3496_acpi_match,
|
||||
},
|
||||
.probe = int3496_probe,
|
||||
.remove = int3496_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(int3496_driver);
|
||||
|
@ -9,6 +9,7 @@
|
||||
* Author: Hema HK <hemahk@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -237,7 +238,11 @@ static int palmas_usb_probe(struct platform_device *pdev)
|
||||
palmas_usb->sw_debounce_jiffies = msecs_to_jiffies(debounce);
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&palmas_usb->wq_detectid, palmas_gpio_id_detect);
|
||||
status = devm_delayed_work_autocancel(&pdev->dev,
|
||||
&palmas_usb->wq_detectid,
|
||||
palmas_gpio_id_detect);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
palmas->usb = palmas_usb;
|
||||
palmas_usb->palmas = palmas;
|
||||
@ -359,15 +364,6 @@ static int palmas_usb_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int palmas_usb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&palmas_usb->wq_detectid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int palmas_usb_suspend(struct device *dev)
|
||||
{
|
||||
@ -422,7 +418,6 @@ static const struct of_device_id of_palmas_match_tbl[] = {
|
||||
|
||||
static struct platform_driver palmas_usb_driver = {
|
||||
.probe = palmas_usb_probe,
|
||||
.remove = palmas_usb_remove,
|
||||
.driver = {
|
||||
.name = "palmas-usb",
|
||||
.of_match_table = of_palmas_match_tbl,
|
||||
|
@ -7,6 +7,7 @@
|
||||
* Stephen Boyd <stephen.boyd@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/extcon-provider.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -116,7 +117,11 @@ static int qcom_usb_extcon_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
info->debounce_jiffies = msecs_to_jiffies(USB_ID_DEBOUNCE_MS);
|
||||
INIT_DELAYED_WORK(&info->wq_detcable, qcom_usb_extcon_detect_cable);
|
||||
|
||||
ret = devm_delayed_work_autocancel(dev, &info->wq_detcable,
|
||||
qcom_usb_extcon_detect_cable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info->id_irq = platform_get_irq_byname(pdev, "usb_id");
|
||||
if (info->id_irq > 0) {
|
||||
@ -158,15 +163,6 @@ static int qcom_usb_extcon_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_usb_extcon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_usb_extcon_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&info->wq_detcable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int qcom_usb_extcon_suspend(struct device *dev)
|
||||
{
|
||||
@ -210,7 +206,6 @@ MODULE_DEVICE_TABLE(of, qcom_usb_extcon_dt_match);
|
||||
|
||||
static struct platform_driver qcom_usb_extcon_driver = {
|
||||
.probe = qcom_usb_extcon_probe,
|
||||
.remove = qcom_usb_extcon_remove,
|
||||
.driver = {
|
||||
.name = "extcon-pm8941-misc",
|
||||
.pm = &qcom_usb_extcon_pm_ops,
|
||||
|
@ -7,6 +7,7 @@
|
||||
* Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com>
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/module.h>
|
||||
@ -106,6 +107,7 @@ static int rpi_hwmon_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rpi_hwmon_data *data;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
@ -119,7 +121,10 @@ static int rpi_hwmon_probe(struct platform_device *pdev)
|
||||
&rpi_chip_info,
|
||||
NULL);
|
||||
|
||||
INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll);
|
||||
ret = devm_delayed_work_autocancel(dev, &data->get_values_poll_work,
|
||||
get_values_poll);
|
||||
if (ret)
|
||||
return ret;
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
if (!PTR_ERR_OR_ZERO(data->hwmon_dev))
|
||||
@ -128,18 +133,8 @@ static int rpi_hwmon_probe(struct platform_device *pdev)
|
||||
return PTR_ERR_OR_ZERO(data->hwmon_dev);
|
||||
}
|
||||
|
||||
static int rpi_hwmon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rpi_hwmon_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&data->get_values_poll_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rpi_hwmon_driver = {
|
||||
.probe = rpi_hwmon_probe,
|
||||
.remove = rpi_hwmon_remove,
|
||||
.driver = {
|
||||
.name = "raspberrypi-hwmon",
|
||||
},
|
||||
|
@ -79,8 +79,8 @@ static void cio2_bridge_create_fwnode_properties(
|
||||
{
|
||||
sensor->prop_names = prop_names;
|
||||
|
||||
sensor->local_ref[0].node = &sensor->swnodes[SWNODE_CIO2_ENDPOINT];
|
||||
sensor->remote_ref[0].node = &sensor->swnodes[SWNODE_SENSOR_ENDPOINT];
|
||||
sensor->local_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_CIO2_ENDPOINT]);
|
||||
sensor->remote_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_SENSOR_ENDPOINT]);
|
||||
|
||||
sensor->dev_properties[0] = PROPERTY_ENTRY_U32(
|
||||
sensor->prop_names.clock_frequency,
|
||||
|
@ -1038,6 +1038,25 @@ static bool of_is_ancestor_of(struct device_node *test_ancestor,
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct device_node *of_get_compat_node(struct device_node *np)
|
||||
{
|
||||
of_node_get(np);
|
||||
|
||||
while (np) {
|
||||
if (!of_device_is_available(np)) {
|
||||
of_node_put(np);
|
||||
np = NULL;
|
||||
}
|
||||
|
||||
if (of_find_property(np, "compatible", NULL))
|
||||
break;
|
||||
|
||||
np = of_get_next_parent(np);
|
||||
}
|
||||
|
||||
return np;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_link_to_phandle - Add fwnode link to supplier from supplier phandle
|
||||
* @con_np: consumer device tree node
|
||||
@ -1061,25 +1080,11 @@ static int of_link_to_phandle(struct device_node *con_np,
|
||||
struct device *sup_dev;
|
||||
struct device_node *tmp_np = sup_np;
|
||||
|
||||
of_node_get(sup_np);
|
||||
/*
|
||||
* Find the device node that contains the supplier phandle. It may be
|
||||
* @sup_np or it may be an ancestor of @sup_np.
|
||||
*/
|
||||
while (sup_np) {
|
||||
|
||||
/* Don't allow linking to a disabled supplier */
|
||||
if (!of_device_is_available(sup_np)) {
|
||||
of_node_put(sup_np);
|
||||
sup_np = NULL;
|
||||
}
|
||||
|
||||
if (of_find_property(sup_np, "compatible", NULL))
|
||||
break;
|
||||
|
||||
sup_np = of_get_next_parent(sup_np);
|
||||
}
|
||||
|
||||
sup_np = of_get_compat_node(sup_np);
|
||||
if (!sup_np) {
|
||||
pr_debug("Not linking %pOFP to %pOFP - No device\n",
|
||||
con_np, tmp_np);
|
||||
@ -1225,6 +1230,8 @@ static struct device_node *parse_##fname(struct device_node *np, \
|
||||
* @parse_prop.prop_name: Name of property holding a phandle value
|
||||
* @parse_prop.index: For properties holding a list of phandles, this is the
|
||||
* index into the list
|
||||
* @optional: The property can be an optional dependency.
|
||||
* @node_not_dev: The consumer node containing the property is never a device.
|
||||
*
|
||||
* Returns:
|
||||
* parse_prop() return values are
|
||||
@ -1236,6 +1243,7 @@ struct supplier_bindings {
|
||||
struct device_node *(*parse_prop)(struct device_node *np,
|
||||
const char *prop_name, int index);
|
||||
bool optional;
|
||||
bool node_not_dev;
|
||||
};
|
||||
|
||||
DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells")
|
||||
@ -1260,6 +1268,7 @@ DEFINE_SIMPLE_PROP(pinctrl5, "pinctrl-5", NULL)
|
||||
DEFINE_SIMPLE_PROP(pinctrl6, "pinctrl-6", NULL)
|
||||
DEFINE_SIMPLE_PROP(pinctrl7, "pinctrl-7", NULL)
|
||||
DEFINE_SIMPLE_PROP(pinctrl8, "pinctrl-8", NULL)
|
||||
DEFINE_SIMPLE_PROP(remote_endpoint, "remote-endpoint", NULL)
|
||||
DEFINE_SUFFIX_PROP(regulators, "-supply", NULL)
|
||||
DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells")
|
||||
|
||||
@ -1343,6 +1352,7 @@ static const struct supplier_bindings of_supplier_bindings[] = {
|
||||
{ .parse_prop = parse_pinctrl6, },
|
||||
{ .parse_prop = parse_pinctrl7, },
|
||||
{ .parse_prop = parse_pinctrl8, },
|
||||
{ .parse_prop = parse_remote_endpoint, .node_not_dev = true, },
|
||||
{ .parse_prop = parse_gpio_compat, },
|
||||
{ .parse_prop = parse_interrupts, },
|
||||
{ .parse_prop = parse_regulators, },
|
||||
@ -1387,10 +1397,16 @@ static int of_link_property(struct device_node *con_np, const char *prop_name)
|
||||
}
|
||||
|
||||
while ((phandle = s->parse_prop(con_np, prop_name, i))) {
|
||||
struct device_node *con_dev_np;
|
||||
|
||||
con_dev_np = s->node_not_dev
|
||||
? of_get_compat_node(con_np)
|
||||
: of_node_get(con_np);
|
||||
matched = true;
|
||||
i++;
|
||||
of_link_to_phandle(con_np, phandle);
|
||||
of_link_to_phandle(con_dev_np, phandle);
|
||||
of_node_put(phandle);
|
||||
of_node_put(con_dev_np);
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
@ -124,7 +125,7 @@ static void gpd_pocket_fan_force_update(struct gpd_pocket_fan_data *fan)
|
||||
static int gpd_pocket_fan_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpd_pocket_fan_data *fan;
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(temp_limits); i++) {
|
||||
if (temp_limits[i] < 20000 || temp_limits[i] > 90000) {
|
||||
@ -152,7 +153,10 @@ static int gpd_pocket_fan_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
fan->dev = &pdev->dev;
|
||||
INIT_DELAYED_WORK(&fan->work, gpd_pocket_fan_worker);
|
||||
ret = devm_delayed_work_autocancel(&pdev->dev, &fan->work,
|
||||
gpd_pocket_fan_worker);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Note this returns a "weak" reference which we don't need to free */
|
||||
fan->dts0 = thermal_zone_get_zone_by_name("soc_dts0");
|
||||
@ -177,14 +181,6 @@ static int gpd_pocket_fan_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpd_pocket_fan_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gpd_pocket_fan_data *fan = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&fan->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int gpd_pocket_fan_suspend(struct device *dev)
|
||||
{
|
||||
@ -215,7 +211,6 @@ MODULE_DEVICE_TABLE(acpi, gpd_pocket_fan_acpi_match);
|
||||
|
||||
static struct platform_driver gpd_pocket_fan_driver = {
|
||||
.probe = gpd_pocket_fan_probe,
|
||||
.remove = gpd_pocket_fan_remove,
|
||||
.driver = {
|
||||
.name = "gpd_pocket_fan",
|
||||
.acpi_match_table = gpd_pocket_fan_acpi_match,
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -593,7 +594,11 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||
power->axp20x_id = axp_data->axp20x_id;
|
||||
power->regmap = axp20x->regmap;
|
||||
power->num_irqs = axp_data->num_irq_names;
|
||||
INIT_DELAYED_WORK(&power->vbus_detect, axp20x_usb_power_poll_vbus);
|
||||
|
||||
ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect,
|
||||
axp20x_usb_power_poll_vbus);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (power->axp20x_id == AXP202_ID) {
|
||||
/* Enable vbus valid checking */
|
||||
@ -652,15 +657,6 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axp20x_usb_power_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct axp20x_usb_power *power = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&power->vbus_detect);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id axp20x_usb_power_match[] = {
|
||||
{
|
||||
.compatible = "x-powers,axp202-usb-power-supply",
|
||||
@ -680,7 +676,6 @@ MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
|
||||
|
||||
static struct platform_driver axp20x_usb_power_driver = {
|
||||
.probe = axp20x_usb_power_probe,
|
||||
.remove = axp20x_usb_power_remove,
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
.of_match_table = axp20x_usb_power_match,
|
||||
|
@ -17,6 +17,7 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
@ -473,7 +474,11 @@ static int bq24735_charger_probe(struct i2c_client *client,
|
||||
if (!charger->poll_interval)
|
||||
return 0;
|
||||
|
||||
INIT_DELAYED_WORK(&charger->poll, bq24735_poll);
|
||||
ret = devm_delayed_work_autocancel(&client->dev, &charger->poll,
|
||||
bq24735_poll);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
schedule_delayed_work(&charger->poll,
|
||||
msecs_to_jiffies(charger->poll_interval));
|
||||
}
|
||||
@ -481,16 +486,6 @@ static int bq24735_charger_probe(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq24735_charger_remove(struct i2c_client *client)
|
||||
{
|
||||
struct bq24735 *charger = i2c_get_clientdata(client);
|
||||
|
||||
if (charger->poll_interval)
|
||||
cancel_delayed_work_sync(&charger->poll);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bq24735_charger_id[] = {
|
||||
{ "bq24735-charger", 0 },
|
||||
{}
|
||||
@ -509,7 +504,6 @@ static struct i2c_driver bq24735_charger_driver = {
|
||||
.of_match_table = bq24735_match_ids,
|
||||
},
|
||||
.probe = bq24735_charger_probe,
|
||||
.remove = bq24735_charger_remove,
|
||||
.id_table = bq24735_charger_id,
|
||||
};
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
* Author: Auryn Verwegen
|
||||
* Author: Mike Looijmans
|
||||
*/
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
@ -445,15 +446,6 @@ static enum power_supply_property ltc294x_properties[] = {
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
};
|
||||
|
||||
static int ltc294x_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ltc294x_info *info = i2c_get_clientdata(client);
|
||||
|
||||
cancel_delayed_work_sync(&info->work);
|
||||
power_supply_unregister(info->supply);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc294x_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -547,7 +539,10 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
|
||||
|
||||
psy_cfg.drv_data = info;
|
||||
|
||||
INIT_DELAYED_WORK(&info->work, ltc294x_work);
|
||||
ret = devm_delayed_work_autocancel(&client->dev, &info->work,
|
||||
ltc294x_work);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ltc294x_reset(info, prescaler_exp);
|
||||
if (ret < 0) {
|
||||
@ -555,8 +550,8 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->supply = power_supply_register(&client->dev, &info->supply_desc,
|
||||
&psy_cfg);
|
||||
info->supply = devm_power_supply_register(&client->dev,
|
||||
&info->supply_desc, &psy_cfg);
|
||||
if (IS_ERR(info->supply)) {
|
||||
dev_err(&client->dev, "failed to register ltc2941\n");
|
||||
return PTR_ERR(info->supply);
|
||||
@ -655,7 +650,6 @@ static struct i2c_driver ltc294x_driver = {
|
||||
.pm = LTC294X_PM_OPS,
|
||||
},
|
||||
.probe = ltc294x_i2c_probe,
|
||||
.remove = ltc294x_i2c_remove,
|
||||
.shutdown = ltc294x_i2c_shutdown,
|
||||
.id_table = ltc294x_i2c_id,
|
||||
};
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
@ -1165,7 +1166,10 @@ skip_gpio:
|
||||
}
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&chip->work, sbs_delayed_work);
|
||||
rc = devm_delayed_work_autocancel(&client->dev, &chip->work,
|
||||
sbs_delayed_work);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
chip->power_supply = devm_power_supply_register(&client->dev, sbs_desc,
|
||||
&psy_cfg);
|
||||
@ -1185,15 +1189,6 @@ exit_psupply:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sbs_remove(struct i2c_client *client)
|
||||
{
|
||||
struct sbs_info *chip = i2c_get_clientdata(client);
|
||||
|
||||
cancel_delayed_work_sync(&chip->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined CONFIG_PM_SLEEP
|
||||
|
||||
static int sbs_suspend(struct device *dev)
|
||||
@ -1248,7 +1243,6 @@ MODULE_DEVICE_TABLE(of, sbs_dt_ids);
|
||||
|
||||
static struct i2c_driver sbs_battery_driver = {
|
||||
.probe_new = sbs_probe,
|
||||
.remove = sbs_remove,
|
||||
.alert = sbs_alert,
|
||||
.id_table = sbs_id,
|
||||
.driver = {
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -1842,7 +1843,10 @@ static int spmi_regulator_of_parse(struct device_node *node,
|
||||
return ret;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&vreg->ocp_work, spmi_regulator_vs_ocp_work);
|
||||
ret = devm_delayed_work_autocancel(dev, &vreg->ocp_work,
|
||||
spmi_regulator_vs_ocp_work);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -2157,10 +2161,8 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
|
||||
vreg->regmap = regmap;
|
||||
if (reg->ocp) {
|
||||
vreg->ocp_irq = platform_get_irq_byname(pdev, reg->ocp);
|
||||
if (vreg->ocp_irq < 0) {
|
||||
ret = vreg->ocp_irq;
|
||||
goto err;
|
||||
}
|
||||
if (vreg->ocp_irq < 0)
|
||||
return vreg->ocp_irq;
|
||||
}
|
||||
vreg->desc.id = -1;
|
||||
vreg->desc.owner = THIS_MODULE;
|
||||
@ -2203,8 +2205,7 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
|
||||
rdev = devm_regulator_register(dev, &vreg->desc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(dev, "failed to register %s\n", name);
|
||||
ret = PTR_ERR(rdev);
|
||||
goto err;
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&vreg->node);
|
||||
@ -2212,24 +2213,6 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
list_for_each_entry(vreg, vreg_list, node)
|
||||
if (vreg->ocp_irq)
|
||||
cancel_delayed_work_sync(&vreg->ocp_work);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_spmi_regulator_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spmi_regulator *vreg;
|
||||
struct list_head *vreg_list = platform_get_drvdata(pdev);
|
||||
|
||||
list_for_each_entry(vreg, vreg_list, node)
|
||||
if (vreg->ocp_irq)
|
||||
cancel_delayed_work_sync(&vreg->ocp_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver qcom_spmi_regulator_driver = {
|
||||
@ -2238,7 +2221,6 @@ static struct platform_driver qcom_spmi_regulator_driver = {
|
||||
.of_match_table = qcom_spmi_regulator_match,
|
||||
},
|
||||
.probe = qcom_spmi_regulator_probe,
|
||||
.remove = qcom_spmi_regulator_remove,
|
||||
};
|
||||
module_platform_driver(qcom_spmi_regulator_driver);
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
* Rewritten by Aaro Koskinen.
|
||||
*/
|
||||
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/device.h>
|
||||
@ -127,9 +128,12 @@ static int retu_wdt_probe(struct platform_device *pdev)
|
||||
wdev->rdev = rdev;
|
||||
wdev->dev = &pdev->dev;
|
||||
|
||||
INIT_DELAYED_WORK(&wdev->ping_work, retu_wdt_ping_work);
|
||||
ret = devm_delayed_work_autocancel(&pdev->dev, &wdev->ping_work,
|
||||
retu_wdt_ping_work);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = watchdog_register_device(retu_wdt);
|
||||
ret = devm_watchdog_register_device(&pdev->dev, retu_wdt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -138,25 +142,11 @@ static int retu_wdt_probe(struct platform_device *pdev)
|
||||
else
|
||||
retu_wdt_ping_enable(wdev);
|
||||
|
||||
platform_set_drvdata(pdev, retu_wdt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int retu_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct watchdog_device *wdog = platform_get_drvdata(pdev);
|
||||
struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
|
||||
|
||||
watchdog_unregister_device(wdog);
|
||||
cancel_delayed_work_sync(&wdev->ping_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver retu_wdt_driver = {
|
||||
.probe = retu_wdt_probe,
|
||||
.remove = retu_wdt_remove,
|
||||
.driver = {
|
||||
.name = "retu-wdt",
|
||||
},
|
||||
|
@ -773,7 +773,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_atomic_t);
|
||||
ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[3];
|
||||
char buf[2];
|
||||
bool val;
|
||||
int r;
|
||||
struct dentry *dentry = F_DENTRY(file);
|
||||
@ -789,7 +789,6 @@ ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf,
|
||||
else
|
||||
buf[0] = 'N';
|
||||
buf[1] = '\n';
|
||||
buf[2] = 0x00;
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_read_file_bool);
|
||||
|
@ -35,7 +35,7 @@
|
||||
static struct vfsmount *debugfs_mount;
|
||||
static int debugfs_mount_count;
|
||||
static bool debugfs_registered;
|
||||
static unsigned int debugfs_allow = DEFAULT_DEBUGFS_ALLOW_BITS;
|
||||
static unsigned int debugfs_allow __ro_after_init = DEFAULT_DEBUGFS_ALLOW_BITS;
|
||||
|
||||
/*
|
||||
* Don't allow access attributes to be changed whilst the kernel is locked down
|
||||
|
@ -49,7 +49,7 @@ struct dev_iommu;
|
||||
/**
|
||||
* struct subsys_interface - interfaces to device functions
|
||||
* @name: name of the device function
|
||||
* @subsys: subsytem of the devices to attach to
|
||||
* @subsys: subsystem of the devices to attach to
|
||||
* @node: the list of functions registered at the subsystem
|
||||
* @add_dev: device hookup to device function handler
|
||||
* @remove_dev: device hookup to device function handler
|
||||
@ -439,6 +439,9 @@ struct dev_links_info {
|
||||
* @state_synced: The hardware state of this device has been synced to match
|
||||
* the software state of this device by calling the driver/bus
|
||||
* sync_state() callback.
|
||||
* @can_match: The device has matched with a driver at least once or it is in
|
||||
* a bus (like AMBA) which can't check for matching drivers until
|
||||
* other devices probe successfully.
|
||||
* @dma_coherent: this particular device is dma coherent, even if the
|
||||
* architecture supports non-coherent devices.
|
||||
* @dma_ops_bypass: If set to %true then the dma_ops are bypassed for the
|
||||
@ -545,6 +548,7 @@ struct device {
|
||||
bool offline:1;
|
||||
bool of_node_reused:1;
|
||||
bool state_synced:1;
|
||||
bool can_match:1;
|
||||
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
|
||||
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
|
||||
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
|
||||
|
54
include/linux/devm-helpers.h
Normal file
54
include/linux/devm-helpers.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef __LINUX_DEVM_HELPERS_H
|
||||
#define __LINUX_DEVM_HELPERS_H
|
||||
|
||||
/*
|
||||
* Functions which do automatically cancel operations or release resources upon
|
||||
* driver detach.
|
||||
*
|
||||
* These should be helpful to avoid mixing the manual and devm-based resource
|
||||
* management which can be source of annoying, rarely occurring,
|
||||
* hard-to-reproduce bugs.
|
||||
*
|
||||
* Please take into account that devm based cancellation may be performed some
|
||||
* time after the remove() is ran.
|
||||
*
|
||||
* Thus mixing devm and manual resource management can easily cause problems
|
||||
* when unwinding operations with dependencies. IRQ scheduling a work in a queue
|
||||
* is typical example where IRQs are often devm-managed and WQs are manually
|
||||
* cleaned at remove(). If IRQs are not manually freed at remove() (and this is
|
||||
* often the case when we use devm for IRQs) we have a period of time after
|
||||
* remove() - and before devm managed IRQs are freed - where new IRQ may fire
|
||||
* and schedule a work item which won't be cancelled because remove() was
|
||||
* already ran.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
static inline void devm_delayed_work_drop(void *res)
|
||||
{
|
||||
cancel_delayed_work_sync(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_delayed_work_autocancel - Resource-managed delayed work allocation
|
||||
* @dev: Device which lifetime work is bound to
|
||||
* @w: Work item to be queued
|
||||
* @worker: Worker function
|
||||
*
|
||||
* Initialize delayed work which is automatically cancelled when driver is
|
||||
* detached. A few drivers need delayed work which must be cancelled before
|
||||
* driver is detached to avoid accessing removed resources.
|
||||
* devm_delayed_work_autocancel() can be used to omit the explicit
|
||||
* cancelleation when driver is detached.
|
||||
*/
|
||||
static inline int devm_delayed_work_autocancel(struct device *dev,
|
||||
struct delayed_work *w,
|
||||
work_func_t worker)
|
||||
{
|
||||
INIT_DELAYED_WORK(w, worker);
|
||||
return devm_add_action(dev, devm_delayed_work_drop, w);
|
||||
}
|
||||
|
||||
#endif
|
@ -359,4 +359,7 @@ static inline int is_sh_early_platform_device(struct platform_device *pdev)
|
||||
}
|
||||
#endif /* CONFIG_SUPERH */
|
||||
|
||||
/* For now only SuperH uses it */
|
||||
void early_platform_cleanup(void);
|
||||
|
||||
#endif /* _PLATFORM_DEVICE_H_ */
|
||||
|
@ -254,6 +254,13 @@ struct software_node_ref_args {
|
||||
u64 args[NR_FWNODE_REFERENCE_ARGS];
|
||||
};
|
||||
|
||||
#define SOFTWARE_NODE_REFERENCE(_ref_, ...) \
|
||||
(const struct software_node_ref_args) { \
|
||||
.node = _ref_, \
|
||||
.nargs = ARRAY_SIZE(((u64[]){ 0, ##__VA_ARGS__ })) - 1, \
|
||||
.args = { __VA_ARGS__ }, \
|
||||
}
|
||||
|
||||
/**
|
||||
* struct property_entry - "Built-in" device property representation.
|
||||
* @name: Name of the property.
|
||||
@ -362,11 +369,7 @@ struct property_entry {
|
||||
.name = _name_, \
|
||||
.length = sizeof(struct software_node_ref_args), \
|
||||
.type = DEV_PROP_REF, \
|
||||
{ .pointer = &(const struct software_node_ref_args) { \
|
||||
.node = _ref_, \
|
||||
.nargs = ARRAY_SIZE(((u64[]){ 0, ##__VA_ARGS__ })) - 1, \
|
||||
.args = { __VA_ARGS__ }, \
|
||||
} }, \
|
||||
{ .pointer = &SOFTWARE_NODE_REFERENCE(_ref_, ##__VA_ARGS__), }, \
|
||||
}
|
||||
|
||||
struct property_entry *
|
||||
|
@ -251,12 +251,13 @@ static int kobj_usermode_filter(struct kobject *kobj)
|
||||
|
||||
static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem)
|
||||
{
|
||||
int buffer_size = sizeof(env->buf) - env->buflen;
|
||||
int len;
|
||||
|
||||
len = strlcpy(&env->buf[env->buflen], subsystem,
|
||||
sizeof(env->buf) - env->buflen);
|
||||
if (len >= (sizeof(env->buf) - env->buflen)) {
|
||||
WARN(1, KERN_ERR "init_uevent_argv: buffer size too small\n");
|
||||
len = strlcpy(&env->buf[env->buflen], subsystem, buffer_size);
|
||||
if (len >= buffer_size) {
|
||||
pr_warn("init_uevent_argv: buffer size of %d too small, needed %d\n",
|
||||
buffer_size, len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ static bool test_fw_in_ns(const char *fw_name, const char *sys_path, bool block_
|
||||
}
|
||||
if (block_fw_in_parent_ns)
|
||||
umount("/lib/firmware");
|
||||
return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false;
|
||||
return WEXITSTATUS(status) == EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (unshare(CLONE_NEWNS) != 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user