summaryrefslogtreecommitdiff
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/base.h3
-rw-r--r--drivers/base/bus.c102
-rw-r--r--drivers/base/cacheinfo.c28
-rw-r--r--drivers/base/core.c30
-rw-r--r--drivers/base/cpu.c12
-rw-r--r--drivers/base/dd.c90
-rw-r--r--drivers/base/devtmpfs.c108
-rw-r--r--drivers/base/memory.c9
-rw-r--r--drivers/base/node.c17
-rw-r--r--drivers/base/platform.c19
-rw-r--r--drivers/base/power/clock_ops.c2
-rw-r--r--drivers/base/power/main.c32
-rw-r--r--drivers/base/power/runtime-test.c57
-rw-r--r--drivers/base/power/runtime.c109
-rw-r--r--drivers/base/property.c4
-rw-r--r--drivers/base/regmap/internal.h12
-rw-r--r--drivers/base/regmap/regcache-maple.c9
-rw-r--r--drivers/base/regmap/regcache-rbtree.c4
-rw-r--r--drivers/base/regmap/regcache.c125
-rw-r--r--drivers/base/regmap/regmap-debugfs.c119
-rw-r--r--drivers/base/regmap/regmap-irq.c2
-rw-r--r--drivers/base/regmap/regmap-kunit.c98
-rw-r--r--drivers/base/regmap/regmap-ram.c26
-rw-r--r--drivers/base/regmap/regmap-raw-ram.c21
-rw-r--r--drivers/base/regmap/regmap.c212
-rw-r--r--drivers/base/test/Kconfig1
-rw-r--r--drivers/base/test/property-entry-test.c3
-rw-r--r--drivers/base/transport_class.c2
28 files changed, 754 insertions, 502 deletions
diff --git a/drivers/base/base.h b/drivers/base/base.h
index a5b7abc10ff0..866b2beb6baa 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -27,6 +27,7 @@
* @drivers_autoprobe: gate whether new devices are automatically attached to
* registered drivers, or new drivers automatically attach
* to existing devices.
+ * @no_drivers: gate whether drivers can be registered.
* @bus: pointer back to the struct bus_type that this structure is associated
* with.
* @dev_root: Default device to use as the parent.
@@ -51,6 +52,7 @@ struct subsys_private {
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
+ unsigned int no_drivers:1;
const struct bus_type *bus;
struct device *dev_root;
@@ -154,6 +156,7 @@ struct kobject *virtual_device_parent(void);
int bus_add_device(struct device *dev);
void bus_probe_device(struct device *dev);
+void bus_probe_device_deferred(struct device *dev);
void bus_remove_device(struct device *dev);
void bus_notify(struct device *dev, enum bus_notifier_event value);
bool bus_is_registered(const struct bus_type *bus);
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index d17bd91490ee..f8759d5c9056 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -19,6 +19,7 @@
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
+#include <linux/panic.h>
#include "base.h"
#include "power/power.h"
@@ -241,6 +242,7 @@ static ssize_t unbind_store(struct device_driver *drv, const char *buf,
dev = bus_find_device_by_name(bus, NULL, buf);
if (dev && dev->driver == drv) {
+ add_taint_module(drv->owner, TAINT_FORCED_BIND, LOCKDEP_STILL_OK);
device_driver_detach(dev);
err = count;
}
@@ -264,6 +266,7 @@ static ssize_t bind_store(struct device_driver *drv, const char *buf,
dev = bus_find_device_by_name(bus, NULL, buf);
if (dev && driver_match_device(drv, dev)) {
+ add_taint_module(drv->owner, TAINT_FORCED_BIND, LOCKDEP_STILL_OK);
err = device_driver_attach(drv, dev);
if (!err) {
/* success */
@@ -603,13 +606,7 @@ out_put:
return error;
}
-/**
- * bus_probe_device - probe drivers for a new device
- * @dev: device to probe
- *
- * - Automatically probe for a driver if the bus allows it.
- */
-void bus_probe_device(struct device *dev)
+static void __bus_probe_device(struct device *dev)
{
struct subsys_private *sp = bus_to_subsys(dev->bus);
struct subsys_interface *sif;
@@ -628,6 +625,26 @@ void bus_probe_device(struct device *dev)
}
/**
+ * bus_probe_device - probe drivers for a new device
+ * @dev: device to probe
+ *
+ * - Automatically probe for a driver if the bus allows it.
+ */
+void bus_probe_device(struct device *dev)
+{
+ __bus_probe_device(dev);
+}
+
+void bus_probe_device_deferred(struct device *dev)
+{
+ if (dev->type && dev->type->need_parent_lock)
+ device_lock(dev->parent);
+ __bus_probe_device(dev);
+ if (dev->type && dev->type->need_parent_lock)
+ device_unlock(dev->parent);
+}
+
+/**
* bus_remove_device - remove device from bus
* @dev: device to be removed
*
@@ -738,6 +755,11 @@ int bus_add_driver(struct device_driver *drv)
if (!sp)
return -EINVAL;
+ if (sp->no_drivers) {
+ error = -ENXIO;
+ goto out_put_bus;
+ }
+
/*
* Reference in sp is now incremented and will be dropped when
* the driver is removed from the bus
@@ -849,10 +871,10 @@ static int __must_check bus_rescan_devices_helper(struct device *dev,
int ret = 0;
if (!dev->driver) {
- if (dev->parent && dev->bus->need_parent_lock)
+ if (dev->type && dev->type->need_parent_lock)
device_lock(dev->parent);
ret = device_attach(dev);
- if (dev->parent && dev->bus->need_parent_lock)
+ if (dev->type && dev->type->need_parent_lock)
device_unlock(dev->parent);
}
return ret < 0 ? ret : 0;
@@ -930,15 +952,7 @@ static ssize_t bus_uevent_store(const struct bus_type *bus,
static struct bus_attribute bus_attr_uevent = __ATTR(uevent, 0200, NULL,
bus_uevent_store);
-/**
- * bus_register - register a driver-core subsystem
- * @bus: bus to register
- *
- * Once we have that, we register the bus with the kobject
- * infrastructure, then register the children subsystems it has:
- * the devices and drivers that belong to the subsystem.
- */
-int bus_register(const struct bus_type *bus)
+static int bus_register_internal(const struct bus_type *bus, bool use_drivers)
{
int retval;
struct subsys_private *priv;
@@ -960,7 +974,8 @@ int bus_register(const struct bus_type *bus)
bus_kobj->kset = bus_kset;
bus_kobj->ktype = &bus_ktype;
- priv->drivers_autoprobe = 1;
+ priv->drivers_autoprobe = use_drivers;
+ priv->no_drivers = !use_drivers;
retval = kset_register(&priv->subsys);
if (retval)
@@ -976,6 +991,10 @@ int bus_register(const struct bus_type *bus)
goto bus_devices_fail;
}
+ /*
+ * Create the drivers kset even if use_drivers is unset to prevent some
+ * older versions of systemd from failing.
+ */
priv->drivers_kset = kset_create_and_add("drivers", NULL, bus_kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
@@ -989,9 +1008,11 @@ int bus_register(const struct bus_type *bus)
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
- retval = add_probe_files(bus);
- if (retval)
- goto bus_probe_files_fail;
+ if (use_drivers) {
+ retval = add_probe_files(bus);
+ if (retval)
+ goto bus_probe_files_fail;
+ }
retval = sysfs_create_groups(bus_kobj, bus->bus_groups);
if (retval)
@@ -1016,9 +1037,41 @@ out:
kfree(priv);
return retval;
}
+
+/**
+ * bus_register - register a driver-core subsystem
+ * @bus: bus to register
+ *
+ * Once we have that, we register the bus with the kobject
+ * infrastructure, then register the children subsystems it has:
+ * the devices and drivers that belong to the subsystem.
+ */
+int bus_register(const struct bus_type *bus)
+{
+ return bus_register_internal(bus, true);
+}
EXPORT_SYMBOL_GPL(bus_register);
/**
+ * companion_bus_register - register a companion bus type
+ * @bus: companion bus to register
+ *
+ * A companion bus is a bus without drivers. Devices that belong to it can be
+ * bound to other devices as their "companions" and represent interfaces that
+ * can be used by the drivers of those other devices. They may also be used for
+ * the enumeration of those other devices.
+ *
+ * The ACPI bus is a specific example of a companion bus.
+ *
+ * Registering a companion bus is like registering a regular bus except that it
+ * skips the creation of sysfs interfaces related to drivers for @bus.
+ */
+int companion_bus_register(const struct bus_type *bus)
+{
+ return bus_register_internal(bus, false);
+}
+
+/**
* bus_unregister - remove a bus from the system
* @bus: bus.
*
@@ -1415,6 +1468,11 @@ struct device_driver *driver_find(const char *name, const struct bus_type *bus)
if (!sp)
return NULL;
+ if (sp->no_drivers) {
+ subsys_put(sp);
+ return NULL;
+ }
+
k = kset_find_obj(sp->drivers_kset, name);
subsys_put(sp);
if (!k)
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c
index 9f9c72727a05..eb8a907f6d50 100644
--- a/drivers/base/cacheinfo.c
+++ b/drivers/base/cacheinfo.c
@@ -38,6 +38,23 @@ struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu)
return ci_cacheinfo(cpu);
}
+/**
+ * get_cpu_cacheinfo_id - Return the cache ID for a CPU and cache level
+ * @cpu: CPU number
+ * @level: Cache level
+ *
+ * The caller must hold the cpuhp lock.
+ *
+ * Return: Cache ID on success, or -1 if no matching cache exists.
+ */
+int get_cpu_cacheinfo_id(int cpu, int level)
+{
+ struct cacheinfo *ci = get_cpu_cacheinfo_level(cpu, level);
+
+ return ci ? ci->id : -1;
+}
+EXPORT_SYMBOL_GPL(get_cpu_cacheinfo_id);
+
static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,
struct cacheinfo *sib_leaf)
{
@@ -1040,9 +1057,10 @@ static int cacheinfo_cpu_online(unsigned int cpu)
rc = cache_add_dev(cpu);
if (rc)
goto err;
- if (cpu_map_shared_cache(true, cpu, &cpu_map))
+ if (cpu_map_shared_cache(true, cpu, &cpu_map)) {
update_per_cpu_data_slice_size(true, cpu, cpu_map);
- sched_update_llc_bytes(cpu);
+ sched_update_llc_bytes(cpu_map);
+ }
return 0;
err:
free_cache_attributes(cpu);
@@ -1059,10 +1077,10 @@ static int cacheinfo_cpu_pre_down(unsigned int cpu)
cpu_cache_sysfs_exit(cpu);
free_cache_attributes(cpu);
- if (nr_shared > 1)
+ if (nr_shared > 1) {
update_per_cpu_data_slice_size(false, cpu, cpu_map);
-
- sched_update_llc_bytes(cpu);
+ sched_update_llc_bytes(cpu_map);
+ }
return 0;
}
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 4c0c373998a1..5daa88fa724a 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -2517,13 +2517,13 @@ static const struct sysfs_ops dev_sysfs_ops = {
.store = dev_attr_store,
};
-#define to_ext_attr(x) container_of(x, struct dev_ext_attribute, attr)
+#define to_ext_attr(x) container_of_const(x, struct dev_ext_attribute, attr)
ssize_t device_store_ulong(struct device *dev,
- struct device_attribute *attr,
+ const struct device_attribute *attr,
const char *buf, size_t size)
{
- struct dev_ext_attribute *ea = to_ext_attr(attr);
+ const struct dev_ext_attribute *ea = to_ext_attr(attr);
int ret;
unsigned long new;
@@ -2537,19 +2537,19 @@ ssize_t device_store_ulong(struct device *dev,
EXPORT_SYMBOL_GPL(device_store_ulong);
ssize_t device_show_ulong(struct device *dev,
- struct device_attribute *attr,
+ const struct device_attribute *attr,
char *buf)
{
- struct dev_ext_attribute *ea = to_ext_attr(attr);
+ const struct dev_ext_attribute *ea = to_ext_attr(attr);
return sysfs_emit(buf, "%lx\n", *(unsigned long *)(ea->var));
}
EXPORT_SYMBOL_GPL(device_show_ulong);
ssize_t device_store_int(struct device *dev,
- struct device_attribute *attr,
+ const struct device_attribute *attr,
const char *buf, size_t size)
{
- struct dev_ext_attribute *ea = to_ext_attr(attr);
+ const struct dev_ext_attribute *ea = to_ext_attr(attr);
int ret;
long new;
@@ -2566,19 +2566,19 @@ ssize_t device_store_int(struct device *dev,
EXPORT_SYMBOL_GPL(device_store_int);
ssize_t device_show_int(struct device *dev,
- struct device_attribute *attr,
+ const struct device_attribute *attr,
char *buf)
{
- struct dev_ext_attribute *ea = to_ext_attr(attr);
+ const struct dev_ext_attribute *ea = to_ext_attr(attr);
return sysfs_emit(buf, "%d\n", *(int *)(ea->var));
}
EXPORT_SYMBOL_GPL(device_show_int);
-ssize_t device_store_bool(struct device *dev, struct device_attribute *attr,
+ssize_t device_store_bool(struct device *dev, const struct device_attribute *attr,
const char *buf, size_t size)
{
- struct dev_ext_attribute *ea = to_ext_attr(attr);
+ const struct dev_ext_attribute *ea = to_ext_attr(attr);
if (kstrtobool(buf, ea->var) < 0)
return -EINVAL;
@@ -2587,19 +2587,19 @@ ssize_t device_store_bool(struct device *dev, struct device_attribute *attr,
}
EXPORT_SYMBOL_GPL(device_store_bool);
-ssize_t device_show_bool(struct device *dev, struct device_attribute *attr,
+ssize_t device_show_bool(struct device *dev, const struct device_attribute *attr,
char *buf)
{
- struct dev_ext_attribute *ea = to_ext_attr(attr);
+ const struct dev_ext_attribute *ea = to_ext_attr(attr);
return sysfs_emit(buf, "%d\n", *(bool *)(ea->var));
}
EXPORT_SYMBOL_GPL(device_show_bool);
ssize_t device_show_string(struct device *dev,
- struct device_attribute *attr, char *buf)
+ const struct device_attribute *attr, char *buf)
{
- struct dev_ext_attribute *ea = to_ext_attr(attr);
+ const struct dev_ext_attribute *ea = to_ext_attr(attr);
return sysfs_emit(buf, "%s\n", (char *)ea->var);
}
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 69e52fed4241..747915ff974f 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -391,6 +391,15 @@ static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env)
}
#endif
+#ifdef CONFIG_PREFERRED_CPU
+static ssize_t preferred_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpu_preferred_mask));
+}
+static DEVICE_ATTR_RO(preferred);
+#endif
+
const struct bus_type cpu_subsys = {
.name = "cpu",
.dev_name = "cpu",
@@ -532,6 +541,9 @@ static struct attribute *cpu_root_attrs[] = {
#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
&dev_attr_modalias.attr,
#endif
+#ifdef CONFIG_PREFERRED_CPU
+ &dev_attr_preferred.attr,
+#endif
NULL
};
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index f6525a7ee8c5..52123251965d 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -86,8 +86,9 @@ static void deferred_probe_work_func(struct work_struct *work)
/*
* This block processes every device in the deferred 'active' list.
* Each device is removed from the active list and passed to
- * bus_probe_device() to re-attempt the probe. The loop continues
- * until every device in the active list is removed and retried.
+ * bus_probe_device_deferred() to re-attempt the probe. The loop
+ * continues until every device in the active list is removed and
+ * retried.
*
* Note: Once the device is removed from the list and the mutex is
* released, it is possible for the device get freed by another thread
@@ -121,7 +122,7 @@ static void deferred_probe_work_func(struct work_struct *work)
device_pm_move_to_tail(dev);
dev_dbg(dev, "Retrying from deferred list\n");
- bus_probe_device(dev);
+ bus_probe_device_deferred(dev);
mutex_lock(&deferred_probe_mutex);
put_device(dev);
@@ -1029,6 +1030,38 @@ static int __device_attach_driver(struct device_driver *drv, void *_data)
return ret == 0;
}
+/*
+ * __device_driver_lock - acquire locks needed to manipulate dev->drv
+ * @dev: Device we will update driver info for
+ * @parent: Parent device. Needed if the device type requires parent lock
+ *
+ * This function will take the required locks for manipulating dev->drv.
+ * Normally this will just be the @dev lock, but when called for a USB
+ * interface, @parent lock will be held as well.
+ */
+static void __device_driver_lock(struct device *dev, struct device *parent)
+{
+ if (dev->type && dev->type->need_parent_lock && parent)
+ device_lock(parent);
+ device_lock(dev);
+}
+
+/*
+ * __device_driver_unlock - release locks needed to manipulate dev->drv
+ * @dev: Device we will update driver info for
+ * @parent: Parent device. Needed if the device type requires parent lock
+ *
+ * This function will release the required locks for manipulating dev->drv.
+ * Normally this will just be the @dev lock, but when called for a
+ * USB interface, @parent lock will be released as well.
+ */
+static void __device_driver_unlock(struct device *dev, struct device *parent)
+{
+ device_unlock(dev);
+ if (dev->type && dev->type->need_parent_lock && parent)
+ device_unlock(parent);
+}
+
static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)
{
struct device *dev = _dev;
@@ -1038,7 +1071,7 @@ static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)
.want_async = true,
};
- device_lock(dev);
+ __device_driver_lock(dev, dev->parent);
/*
* Check if device has already been removed or claimed. This may
@@ -1060,7 +1093,7 @@ static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)
if (dev->parent)
pm_runtime_put(dev->parent);
out_unlock:
- device_unlock(dev);
+ __device_driver_unlock(dev, dev->parent);
put_device(dev);
}
@@ -1117,8 +1150,11 @@ static int __device_attach(struct device *dev, bool allow_async)
}
out_unlock:
device_unlock(dev);
- if (async)
- async_schedule_dev(__device_attach_async_helper, dev);
+ if (async && !async_schedule_dev_nocall(__device_attach_async_helper, dev)) {
+ put_device(dev);
+ ret = 2; /* retry synchronously */
+ }
+
return ret;
}
@@ -1145,48 +1181,20 @@ EXPORT_SYMBOL_GPL(device_attach);
void device_initial_probe(struct device *dev)
{
struct subsys_private *sp = bus_to_subsys(dev->bus);
+ int ret;
if (!sp)
return;
- if (sp->drivers_autoprobe)
- __device_attach(dev, true);
+ if (sp->drivers_autoprobe) {
+ ret = __device_attach(dev, true);
+ if (ret == 2)
+ __device_attach(dev, false);
+ }
subsys_put(sp);
}
-/*
- * __device_driver_lock - acquire locks needed to manipulate dev->drv
- * @dev: Device we will update driver info for
- * @parent: Parent device. Needed if the bus requires parent lock
- *
- * This function will take the required locks for manipulating dev->drv.
- * Normally this will just be the @dev lock, but when called for a USB
- * interface, @parent lock will be held as well.
- */
-static void __device_driver_lock(struct device *dev, struct device *parent)
-{
- if (parent && dev->bus->need_parent_lock)
- device_lock(parent);
- device_lock(dev);
-}
-
-/*
- * __device_driver_unlock - release locks needed to manipulate dev->drv
- * @dev: Device we will update driver info for
- * @parent: Parent device. Needed if the bus requires parent lock
- *
- * This function will release the required locks for manipulating dev->drv.
- * Normally this will just be the @dev lock, but when called for a
- * USB interface, @parent lock will be released as well.
- */
-static void __device_driver_unlock(struct device *dev, struct device *parent)
-{
- device_unlock(dev);
- if (parent && dev->bus->need_parent_lock)
- device_unlock(parent);
-}
-
/**
* device_driver_attach - attach a specific driver to a specific device
* @drv: Driver to attach
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index aef0fcc6aba1..11c70888f38b 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -72,39 +72,90 @@ static struct file_system_type internal_fs_type = {
.kill_sb = kill_anon_super,
};
-/* Simply take a ref on the existing mount */
+struct devtmpfs_context {
+ struct fs_context *fc;
+};
+
+static void devtmpfs_free(struct fs_context *fc)
+{
+ struct devtmpfs_context *ctx = fc->fs_private;
+
+ if (ctx) {
+ put_fs_context(ctx->fc);
+ kfree(ctx);
+ }
+}
+
+static int devtmpfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+ struct devtmpfs_context *ctx = fc->fs_private;
+
+ return ctx->fc->ops->parse_param(ctx->fc, param);
+}
+
+static int devtmpfs_parse_monolithic(struct fs_context *fc, void *data)
+{
+ struct devtmpfs_context *ctx = fc->fs_private;
+
+ if (ctx->fc->ops->parse_monolithic)
+ return ctx->fc->ops->parse_monolithic(ctx->fc, data);
+ return generic_parse_monolithic(ctx->fc, data);
+}
+
static int devtmpfs_get_tree(struct fs_context *fc)
{
+ struct devtmpfs_context *ctx = fc->fs_private;
struct super_block *sb = mnt->mnt_sb;
+ int err;
atomic_inc(&sb->s_active);
down_write(&sb->s_umount);
+
+ if (ctx->fc->ops->reconfigure) {
+ err = ctx->fc->ops->reconfigure(ctx->fc);
+ if (err) {
+ deactivate_locked_super(sb);
+ return err;
+ }
+ }
+
fc->root = dget(sb->s_root);
return 0;
}
-/* Ops are filled in during init depending on underlying shmem or ramfs type */
-static struct fs_context_operations devtmpfs_context_ops = {};
+static const struct fs_context_operations devtmpfs_context_ops = {
+ .free = devtmpfs_free,
+ .parse_param = devtmpfs_parse_param,
+ .parse_monolithic = devtmpfs_parse_monolithic,
+ .get_tree = devtmpfs_get_tree,
+};
-/* Call the underlying initialization and set to our ops */
static int devtmpfs_init_fs_context(struct fs_context *fc)
{
- int ret;
-#ifdef CONFIG_TMPFS
- ret = shmem_init_fs_context(fc);
-#else
- ret = ramfs_init_fs_context(fc);
-#endif
- if (ret < 0)
- return ret;
+ struct devtmpfs_context *ctx;
+ int err;
+
+ ctx = kzalloc_obj(struct devtmpfs_context);
+ if (!ctx)
+ return -ENOMEM;
+
+ /* Each mount will reconfigure the shared superblock w/ new options */
+ ctx->fc = fs_context_for_reconfigure(mnt->mnt_root,
+ mnt->mnt_sb->s_flags, MS_RMT_MASK);
+ if (IS_ERR(ctx->fc)) {
+ err = PTR_ERR(ctx->fc);
+ kfree(ctx);
+ return err;
+ }
+ fc->fs_private = ctx;
fc->ops = &devtmpfs_context_ops;
return 0;
}
static struct file_system_type dev_fs_type = {
- .name = "devtmpfs",
+ .name = "devtmpfs",
.init_fs_context = devtmpfs_init_fs_context,
};
@@ -443,31 +494,6 @@ static int __ref devtmpfsd(void *p)
}
/*
- * Get the underlying (shmem/ramfs) context ops to build ours
- */
-static int devtmpfs_configure_context(void)
-{
- struct fs_context *fc;
-
- fc = fs_context_for_reconfigure(mnt->mnt_root, mnt->mnt_sb->s_flags,
- MS_RMT_MASK);
- if (IS_ERR(fc))
- return PTR_ERR(fc);
-
- /* Set up devtmpfs_context_ops based on underlying type */
- devtmpfs_context_ops.free = fc->ops->free;
- devtmpfs_context_ops.dup = fc->ops->dup;
- devtmpfs_context_ops.parse_param = fc->ops->parse_param;
- devtmpfs_context_ops.parse_monolithic = fc->ops->parse_monolithic;
- devtmpfs_context_ops.get_tree = &devtmpfs_get_tree;
- devtmpfs_context_ops.reconfigure = fc->ops->reconfigure;
-
- put_fs_context(fc);
-
- return 0;
-}
-
-/*
* Create devtmpfs instance, driver-core devices will add their device
* nodes here.
*/
@@ -482,12 +508,6 @@ int __init devtmpfs_init(void)
return PTR_ERR(mnt);
}
- err = devtmpfs_configure_context();
- if (err) {
- pr_err("unable to configure devtmpfs type %d\n", err);
- return err;
- }
-
err = register_filesystem(&dev_fs_type);
if (err) {
pr_err("unable to register devtmpfs type %d\n", err);
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 5eead3346f1e..1ac12c424200 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -255,6 +255,7 @@ static int memory_block_online(struct memory_block *mem)
nr_vmemmap_pages = mem->altmap->free;
mem_hotplug_begin();
+ clear_zone_contiguous(zone);
if (nr_vmemmap_pages) {
ret = mhp_init_memmap_on_memory(start_pfn, nr_vmemmap_pages, zone);
if (ret)
@@ -279,6 +280,7 @@ static int memory_block_online(struct memory_block *mem)
mem->zone = zone;
out:
+ set_zone_contiguous(zone);
mem_hotplug_done();
return ret;
}
@@ -304,6 +306,7 @@ static int memory_block_offline(struct memory_block *mem)
nr_vmemmap_pages = mem->altmap->free;
mem_hotplug_begin();
+ clear_zone_contiguous(mem->zone);
if (nr_vmemmap_pages)
adjust_present_page_count(pfn_to_page(start_pfn), mem->group,
-nr_vmemmap_pages);
@@ -321,8 +324,10 @@ static int memory_block_offline(struct memory_block *mem)
if (nr_vmemmap_pages)
mhp_deinit_memmap_on_memory(start_pfn, nr_vmemmap_pages);
- mem->zone = NULL;
out:
+ set_zone_contiguous(mem->zone);
+ if (!ret)
+ mem->zone = NULL;
mem_hotplug_done();
return ret;
}
@@ -972,7 +977,7 @@ void __init memory_dev_init(void)
* block so that it can be covered.
*/
block_id = ULONG_MAX;
- for_each_present_section_nr(0, nr) {
+ for_each_early_section_nr(0, nr) {
if (block_id != ULONG_MAX && memory_block_id(nr) == block_id)
continue;
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 3da91929ad4e..dcdc8626627a 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -167,20 +167,19 @@ static struct node_access_nodes *node_init_node_access(struct node *node,
dev->parent = &node->dev;
dev->release = node_access_release;
dev->groups = node_access_node_groups;
- if (dev_set_name(dev, "access%u", access))
- goto free;
+ if (dev_set_name(dev, "access%u", access)) {
+ kfree(access_node);
+ return NULL;
+ }
- if (device_register(dev))
- goto free_name;
+ if (device_register(dev)) {
+ put_device(dev);
+ return NULL;
+ }
pm_runtime_no_callbacks(dev);
list_add_tail(&access_node->list_node, &node->access_list);
return access_node;
-free_name:
- kfree_const(dev->kobj.name);
-free:
- kfree(access_node);
- return NULL;
}
#ifdef CONFIG_HMEM_REPORTING
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 66f9ec73d47e..3593d45f4a7a 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -632,16 +632,17 @@ struct platform_device *platform_device_alloc(const char *name, int id)
struct platform_object *pa;
pa = kzalloc(sizeof(*pa) + strlen(name) + 1, GFP_KERNEL);
- if (pa) {
- strcpy(pa->name, name);
- pa->pdev.name = pa->name;
- pa->pdev.id = id;
- device_initialize(&pa->pdev.dev);
- pa->pdev.dev.release = platform_device_release;
- setup_pdev_dma_masks(&pa->pdev);
- }
+ if (!pa)
+ return NULL;
+
+ strcpy(pa->name, name);
+ pa->pdev.name = pa->name;
+ pa->pdev.id = id;
+ device_initialize(&pa->pdev.dev);
+ pa->pdev.dev.release = platform_device_release;
+ setup_pdev_dma_masks(&pa->pdev);
- return pa ? &pa->pdev : NULL;
+ return &pa->pdev;
}
EXPORT_SYMBOL_GPL(platform_device_alloc);
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index 59bb37e8244c..3b8ba27802a8 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -250,7 +250,7 @@ EXPORT_SYMBOL_GPL(pm_clk_add);
*
* Add the clock to the list of clocks used for the power management of @dev.
* The power-management code will take control of the clock reference, so
- * callers should not call clk_put() on @clk after this function sucessfully
+ * callers should not call clk_put() on @clk after this function successfully
* returned.
*/
int pm_clk_add_clk(struct device *dev, struct clk *clk)
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index e130da428141..88a31e09e4f3 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -802,6 +802,7 @@ static void device_resume_noirq(struct device *dev, pm_message_t state, bool asy
const char *info = NULL;
bool skip_resume;
int error = 0;
+ DECLARE_DPM_WATCHDOG_ON_STACK(wd);
TRACE_DEVICE(dev);
TRACE_RESUME(0);
@@ -827,6 +828,7 @@ static void device_resume_noirq(struct device *dev, pm_message_t state, bool asy
if (!dpm_wait_for_superior(dev, async))
goto Out;
+ dpm_watchdog_set(&wd, dev);
skip_resume = dev_pm_skip_resume(dev);
/*
* If the driver callback is skipped below or by the middle layer
@@ -871,6 +873,7 @@ Run:
error = dpm_run_callback(callback, dev, state, info);
Skip:
+ dpm_watchdog_clear(&wd);
dev->power.is_noirq_suspended = false;
Out:
@@ -971,6 +974,7 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
pm_callback_t callback = NULL;
const char *info = NULL;
int error = 0;
+ DECLARE_DPM_WATCHDOG_ON_STACK(wd);
TRACE_DEVICE(dev);
TRACE_RESUME(0);
@@ -987,6 +991,7 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
if (!dpm_wait_for_superior(dev, async))
goto Out;
+ dpm_watchdog_set(&wd, dev);
if (dev->pm_domain) {
info = "early power domain ";
callback = pm_late_early_op(&dev->pm_domain->ops, state);
@@ -1004,7 +1009,7 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
goto Run;
if (dev_pm_skip_resume(dev))
- goto Skip;
+ goto End;
if (dev->driver && dev->driver->pm) {
info = "early driver ";
@@ -1014,6 +1019,9 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
Run:
error = dpm_run_callback(callback, dev, state, info);
+End:
+ dpm_watchdog_clear(&wd);
+
Skip:
dev->power.is_late_suspended = false;
pm_runtime_enable(dev);
@@ -1281,10 +1289,12 @@ static void device_complete(struct device *dev, pm_message_t state)
{
void (*callback)(struct device *) = NULL;
const char *info = NULL;
+ DECLARE_DPM_WATCHDOG_ON_STACK(wd);
if (dev->power.syscore)
goto out;
+ dpm_watchdog_set(&wd, dev);
device_lock(dev);
if (dev->pm_domain) {
@@ -1312,6 +1322,7 @@ static void device_complete(struct device *dev, pm_message_t state)
}
device_unlock(dev);
+ dpm_watchdog_clear(&wd);
out:
/* If enabling runtime PM for the device is blocked, unblock it. */
@@ -1471,6 +1482,7 @@ static pm_message_t resume_event(pm_message_t sleep_state)
case PM_EVENT_QUIESCE:
return PMSG_RECOVER;
case PM_EVENT_HIBERNATE:
+ case PM_EVENT_POWEROFF:
return PMSG_RESTORE;
}
return PMSG_ON;
@@ -1508,6 +1520,7 @@ static void device_suspend_noirq(struct device *dev, pm_message_t state, bool as
pm_callback_t callback = NULL;
const char *info = NULL;
int error = 0;
+ DECLARE_DPM_WATCHDOG_ON_STACK(wd);
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
@@ -1520,6 +1533,7 @@ static void device_suspend_noirq(struct device *dev, pm_message_t state, bool as
if (dev->power.syscore || dev->power.direct_complete)
goto Complete;
+ dpm_watchdog_set(&wd, dev);
if (dev->pm_domain) {
info = "noirq power domain ";
callback = pm_noirq_op(&dev->pm_domain->ops, state);
@@ -1550,7 +1564,7 @@ Run:
WRITE_ONCE(async_error, error);
dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, state, async ? " async noirq" : " noirq", error);
- goto Complete;
+ goto End;
}
Skip:
@@ -1569,6 +1583,9 @@ Skip:
if (dev->power.must_resume)
dpm_superior_set_must_resume(dev);
+End:
+ dpm_watchdog_clear(&wd);
+
Complete:
complete_all(&dev->power.completion);
TRACE_SUSPEND(error);
@@ -1703,6 +1720,7 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
pm_callback_t callback = NULL;
const char *info = NULL;
int error = 0;
+ DECLARE_DPM_WATCHDOG_ON_STACK(wd);
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
@@ -1720,6 +1738,8 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
if (dev->power.direct_complete)
goto Complete;
+ dpm_watchdog_set(&wd, dev);
+
/*
* After this point, any runtime PM operations targeting the device
* will fail until the corresponding pm_runtime_enable() call in
@@ -1761,13 +1781,16 @@ Run:
dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, state, async ? " async late" : " late", error);
pm_runtime_enable(dev);
- goto Complete;
+ goto End;
}
dpm_propagate_wakeup_to_parent(dev);
Skip:
dev->power.is_late_suspended = true;
+End:
+ dpm_watchdog_clear(&wd);
+
Complete:
TRACE_SUSPEND(error);
complete_all(&dev->power.completion);
@@ -2201,6 +2224,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
int (*callback)(struct device *) = NULL;
bool smart_suspend;
int ret = 0;
+ DECLARE_DPM_WATCHDOG_ON_STACK(wd);
/*
* If a device's parent goes into runtime suspend at the wrong time,
@@ -2220,6 +2244,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
if (dev->power.syscore)
return 0;
+ dpm_watchdog_set(&wd, dev);
device_lock(dev);
dev->power.wakeup_path = false;
@@ -2245,6 +2270,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
unlock:
device_unlock(dev);
+ dpm_watchdog_clear(&wd);
if (ret < 0) {
suspend_report_result(dev, callback, ret);
diff --git a/drivers/base/power/runtime-test.c b/drivers/base/power/runtime-test.c
index 1535ad2b0264..24865ce844fc 100644
--- a/drivers/base/power/runtime-test.c
+++ b/drivers/base/power/runtime-test.c
@@ -5,6 +5,7 @@
#include <linux/cleanup.h>
#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
#include <kunit/device.h>
#include <kunit/test.h>
@@ -229,6 +230,61 @@ static void pm_runtime_probe_active_test(struct kunit *test)
KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
}
+static void pm_runtime_supplier_suspend_test(struct kunit *test)
+{
+ struct device *dev = kunit_device_register(test, DEVICE_NAME);
+ struct device *supplier = kunit_device_register(test, DEVICE_NAME "_supplier");
+ struct device *norpm_supplier = kunit_device_register(test, DEVICE_NAME "_norpm_supplier");
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supplier);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, norpm_supplier);
+
+ KUNIT_ASSERT_NOT_NULL(test, device_link_add(dev, supplier, DL_FLAG_PM_RUNTIME));
+ KUNIT_ASSERT_NOT_NULL(test, device_link_add(dev, norpm_supplier, 0));
+
+ pm_runtime_enable(dev);
+ pm_runtime_enable(supplier);
+ pm_runtime_enable(norpm_supplier);
+
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(supplier));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(norpm_supplier));
+
+ /*
+ * Resume the device and RPM-linked supplier; non-RPM-linked supplier
+ * stays suspended.
+ */
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(supplier));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(norpm_supplier));
+
+ /* Also resume the non-RPM-linked supplier. */
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(norpm_supplier));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(norpm_supplier));
+
+ pm_runtime_put_noidle(norpm_supplier);
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(norpm_supplier));
+
+ /*
+ * Suspend device. This should only suspend the device and its
+ * RPM-linked supplier, not the non-RPM-linked supplier.
+ */
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev));
+ /* Note: supplier suspend is async, so we have to flush the queue. */
+ flush_workqueue(pm_wq);
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(supplier));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(norpm_supplier));
+
+ /* Now suspend non-RPM-linked supplier. */
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_suspend(norpm_supplier));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(supplier));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(norpm_supplier));
+}
+
static struct kunit_case pm_runtime_test_cases[] = {
KUNIT_CASE(pm_runtime_depth_test),
KUNIT_CASE(pm_runtime_already_suspended_test),
@@ -236,6 +292,7 @@ static struct kunit_case pm_runtime_test_cases[] = {
KUNIT_CASE(pm_runtime_disabled_test),
KUNIT_CASE(pm_runtime_error_test),
KUNIT_CASE(pm_runtime_probe_active_test),
+ KUNIT_CASE(pm_runtime_supplier_suspend_test),
{}
};
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index fab38bc98113..304a2b123420 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -162,14 +162,14 @@ static void pm_runtime_cancel_pending(struct device *dev)
dev->power.request = RPM_REQ_NONE;
}
-/*
+/**
* pm_runtime_autosuspend_expiration - Get a device's autosuspend-delay expiration time.
* @dev: Device to handle.
*
* Compute the autosuspend-delay expiration time based on the device's
* power.last_busy time. If the delay has already expired or is disabled
* (negative) or the power.use_autosuspend flag isn't set, return 0.
- * Otherwise return the expiration time in nanoseconds (adjusted to be nonzero).
+ * Otherwise return the expiration time in nanoseconds.
*
* This function may be called either with or without dev->power.lock held.
* Either way it can be racy, since power.last_busy may be updated at any time.
@@ -200,7 +200,7 @@ static int dev_memalloc_noio(struct device *dev, void *data)
return dev->power.memalloc_noio;
}
-/*
+/**
* pm_runtime_set_memalloc_noio - Set a device's memalloc_noio flag.
* @dev: Device to handle.
* @enable: True for setting the flag and False for clearing the flag.
@@ -361,7 +361,8 @@ static void rpm_suspend_suppliers(struct device *dev)
list_for_each_entry_rcu(link, &dev->links.suppliers, c_node,
device_links_read_lock_held())
- pm_request_idle(link->supplier);
+ if (device_link_test(link, DL_FLAG_PM_RUNTIME))
+ pm_request_idle(link->supplier);
device_links_read_unlock(idx);
}
@@ -1043,6 +1044,11 @@ static enum hrtimer_restart pm_suspend_timer_fn(struct hrtimer *timer)
* pm_schedule_suspend - Set up a timer to submit a suspend request in future.
* @dev: Device to suspend.
* @delay: Time to wait before submitting a suspend request, in milliseconds.
+ *
+ * Return:
+ * * %1: Success; @dev is already %RPM_SUSPENDED.
+ * * %0: Success.
+ * * Error code on failure.
*/
int pm_schedule_suspend(struct device *dev, unsigned int delay)
{
@@ -1096,16 +1102,15 @@ static int rpm_drop_usage_count(struct device *dev)
}
/**
- * __pm_runtime_idle - Entry point for runtime idle operations.
+ * __pm_runtime_idle - Core entry point for runtime idle operations.
* @dev: Device to send idle notification for.
* @rpmflags: Flag bits.
*
- * If the RPM_GET_PUT flag is set, decrement the device's usage count and
- * return immediately if it is larger than zero (if it becomes negative, log a
- * warning, increment it, and return an error). Then carry out an idle
- * notification, either synchronous or asynchronous.
+ * Carry out an idle check for @dev, either synchronous or asynchronous.
+ * If %RPM_GET_PUT is set in @rpmflags, decrement the device's usage count
+ * first, proceeding with idle notification only if the counter drops to zero.
*
- * This routine may be called in atomic context if the RPM_ASYNC flag is set,
+ * This routine may be called in atomic context if the %RPM_ASYNC flag is set,
* or if pm_runtime_irq_safe() has been called.
*/
int __pm_runtime_idle(struct device *dev, int rpmflags)
@@ -1134,16 +1139,15 @@ int __pm_runtime_idle(struct device *dev, int rpmflags)
EXPORT_SYMBOL_GPL(__pm_runtime_idle);
/**
- * __pm_runtime_suspend - Entry point for runtime put/suspend operations.
+ * __pm_runtime_suspend - Core entry point for runtime put/suspend operations.
* @dev: Device to suspend.
* @rpmflags: Flag bits.
*
- * If the RPM_GET_PUT flag is set, decrement the device's usage count and
- * return immediately if it is larger than zero (if it becomes negative, log a
- * warning, increment it, and return an error). Then carry out a suspend,
- * either synchronous or asynchronous.
+ * Carry out a suspend operation for @dev, either synchronous or asynchronous.
+ * If %RPM_GET_PUT is set in @rpmflags, decrement the device's usage count
+ * first, proceeding with suspend only if the counter drops to zero.
*
- * This routine may be called in atomic context if the RPM_ASYNC flag is set,
+ * This routine may be called in atomic context if the %RPM_ASYNC flag is set,
* or if pm_runtime_irq_safe() has been called.
*/
int __pm_runtime_suspend(struct device *dev, int rpmflags)
@@ -1172,14 +1176,15 @@ int __pm_runtime_suspend(struct device *dev, int rpmflags)
EXPORT_SYMBOL_GPL(__pm_runtime_suspend);
/**
- * __pm_runtime_resume - Entry point for runtime resume operations.
+ * __pm_runtime_resume - Core entry point for runtime resume operations.
* @dev: Device to resume.
* @rpmflags: Flag bits.
*
- * If the RPM_GET_PUT flag is set, increment the device's usage count. Then
- * carry out a resume, either synchronous or asynchronous.
+ * Carry out a runtime resume operation for @dev, either synchronous or
+ * asynchronous. If %RPM_GET_PUT is set in @rpmflags, increment the device's
+ * usage count first, then bring the device to %RPM_ACTIVE state.
*
- * This routine may be called in atomic context if the RPM_ASYNC flag is set,
+ * This routine may be called in atomic context if the %RPM_ASYNC flag is set,
* or if pm_runtime_irq_safe() has been called.
*/
int __pm_runtime_resume(struct device *dev, int rpmflags)
@@ -1249,14 +1254,16 @@ static int pm_runtime_get_conditional(struct device *dev, bool ign_usage_count)
}
/**
- * pm_runtime_get_if_active - Bump up runtime PM usage counter if the device is
- * in active state
+ * pm_runtime_get_if_active - Conditionally bump up runtime PM usage counter.
* @dev: Target device.
*
* Increment the runtime PM usage counter of @dev if its runtime PM status is
- * %RPM_ACTIVE, in which case it returns 1. If the device is in a different
- * state, 0 is returned. -EINVAL is returned if runtime PM is disabled for the
- * device, in which case also the usage_count will remain unmodified.
+ * already %RPM_ACTIVE.
+ *
+ * Return:
+ * * %-EINVAL: Runtime PM is disabled for @dev. The usage counter is not incremented.
+ * * %1: Success; usage counter is incremented.
+ * * %0: @dev was not active.
*/
int pm_runtime_get_if_active(struct device *dev)
{
@@ -1268,17 +1275,15 @@ EXPORT_SYMBOL_GPL(pm_runtime_get_if_active);
* pm_runtime_get_if_in_use - Conditionally bump up runtime PM usage counter.
* @dev: Target device.
*
- * Increment the runtime PM usage counter of @dev if its runtime PM status is
- * %RPM_ACTIVE and its runtime PM usage counter is greater than 0 or it is not
- * ignoring children and its active child count is nonzero. 1 is returned in
- * this case.
- *
- * If @dev is in a different state or it is not in use (that is, its usage
- * counter is 0, or it is ignoring children, or its active child count is 0),
- * 0 is returned.
+ * Increment the runtime PM usage counter of @dev if it is "in use." A device
+ * is considered in use if its runtime PM status is %RPM_ACTIVE and its runtime
+ * PM usage counter is greater than 0, or if it is not ignoring children and
+ * its active child count is nonzero.
*
- * -EINVAL is returned if runtime PM is disabled for the device, in which case
- * also the usage counter of @dev is not updated.
+ * Return:
+ * * %-EINVAL: Runtime PM is disabled for @dev. The usage counter is not incremented.
+ * * %1: Success; usage counter is incremented.
+ * * %0: @dev was not in use; usage counter is not incremented.
*/
int pm_runtime_get_if_in_use(struct device *dev)
{
@@ -1287,7 +1292,7 @@ int pm_runtime_get_if_in_use(struct device *dev)
EXPORT_SYMBOL_GPL(pm_runtime_get_if_in_use);
/**
- * __pm_runtime_set_status - Set runtime PM status of a device.
+ * __pm_runtime_set_status - Set runtime PM status of a device and clear errors.
* @dev: Device to handle.
* @status: New runtime PM status of the device.
*
@@ -1306,7 +1311,7 @@ EXPORT_SYMBOL_GPL(pm_runtime_get_if_in_use);
* If @dev has any suppliers (as reflected by device links to them), and @status
* is RPM_ACTIVE, they will be activated upfront and if the activation of one
* of them fails, the status of @dev will be changed to RPM_SUSPENDED (instead
- * of the @status value) and the suppliers will be deacticated on exit. The
+ * of the @status value) and the suppliers will be deactivated on exit. The
* error returned by the failing supplier activation will be returned in that
* case.
*/
@@ -1462,11 +1467,13 @@ static void __pm_runtime_barrier(struct device *dev)
* pm_runtime_barrier - Flush pending requests and wait for completions.
* @dev: Device to handle.
*
- * Prevent the device from being suspended by incrementing its usage counter and
- * if there's a pending resume request for the device, wake the device up.
- * Next, make sure that all pending requests for the device have been flushed
- * from pm_wq and wait for all runtime PM operations involving the device in
- * progress to complete.
+ * If the device has a pending resume request, resume it synchronously. For all
+ * other request types, cancel any queued request, and wait for running
+ * operations to complete.
+ *
+ * Note that this is intentionally asymmetric, as it guarantees any queued
+ * asynchronous resume request will complete, but it may cancel asynchronous
+ * suspend requests.
*/
void pm_runtime_barrier(struct device *dev)
{
@@ -1550,8 +1557,16 @@ void __pm_runtime_disable(struct device *dev, bool check_resume)
EXPORT_SYMBOL_GPL(__pm_runtime_disable);
/**
- * pm_runtime_enable - Enable runtime PM of a device.
+ * pm_runtime_enable - Enable runtime PM for a device.
* @dev: Device to handle.
+ *
+ * Enable runtime PM transitions for @dev by decrementing its disable counter.
+ * Once the counter reaches zero, the PM core is permitted to execute runtime
+ * PM callbacks for @dev as power conditions change.
+ *
+ * Callers should ensure that the device's runtime PM status accurately reflects
+ * its physical hardware state (via pm_runtime_set_active() or
+ * pm_runtime_set_suspended()) before enabling runtime PM.
*/
void pm_runtime_enable(struct device *dev)
{
@@ -1591,7 +1606,6 @@ static void pm_runtime_set_suspended_action(void *data)
/**
* devm_pm_runtime_set_active_enabled - set_active version of devm_pm_runtime_enable.
- *
* @dev: Device to handle.
*/
int devm_pm_runtime_set_active_enabled(struct device *dev)
@@ -1618,11 +1632,10 @@ static void pm_runtime_disable_action(void *data)
/**
* devm_pm_runtime_enable - devres-enabled version of pm_runtime_enable.
+ * @dev: Device to handle.
*
* NOTE: this will also handle calling pm_runtime_dont_use_autosuspend() for
* you at driver exit time if needed.
- *
- * @dev: Device to handle.
*/
int devm_pm_runtime_enable(struct device *dev)
{
@@ -1639,7 +1652,6 @@ static void pm_runtime_put_noidle_action(void *data)
/**
* devm_pm_runtime_get_noresume - devres-enabled version of pm_runtime_get_noresume.
- *
* @dev: Device to handle.
*/
int devm_pm_runtime_get_noresume(struct device *dev)
@@ -1874,6 +1886,9 @@ void pm_runtime_reinit(struct device *dev)
if (dev->power.runtime_status == RPM_ACTIVE)
pm_runtime_set_suspended(dev);
+ if (dev->power.use_autosuspend)
+ pm_runtime_dont_use_autosuspend(dev);
+
if (dev->power.irq_safe) {
spin_lock_irq(&dev->power.lock);
dev->power.irq_safe = 0;
diff --git a/drivers/base/property.c b/drivers/base/property.c
index b136c339ddae..51109794d727 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -526,7 +526,7 @@ int fwnode_property_match_string(const struct fwnode_handle *fwnode,
if (nval == 0)
return -ENODATA;
- const char **values __free(kfree) = kcalloc(nval, sizeof(*values), GFP_KERNEL);
+ const char **values __free(kfree) = kzalloc_objs(*values, nval);
if (!values)
return -ENOMEM;
@@ -705,7 +705,7 @@ bool fwnode_name_eq(const struct fwnode_handle *fwnode, const char *name)
EXPORT_SYMBOL_GPL(fwnode_name_eq);
/**
- * fwnode_get_parent - Return parent firwmare node
+ * fwnode_get_parent - Return parent firmware node
* @fwnode: Firmware whose parent is retrieved
*
* The caller is responsible for calling fwnode_handle_put() on the returned
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index a6e4689000af..ea375cc8ae87 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -10,6 +10,7 @@
#ifndef _REGMAP_INTERNAL_H
#define _REGMAP_INTERNAL_H
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/fs.h>
@@ -185,6 +186,16 @@ struct regmap {
struct hwspinlock *hwlock;
};
+/*
+ * Scoped guard for the regmap lock. The lock/unlock callbacks are selected
+ * at init time (mutex, spinlock, raw spinlock, hwspinlock or none) and never
+ * fail, so an unconditional guard is sufficient. Use with guard(regmap)(map)
+ * or scoped_guard(regmap, map) { ... }.
+ */
+DEFINE_GUARD(regmap, struct regmap *,
+ _T->lock(_T->lock_arg),
+ _T->unlock(_T->lock_arg))
+
struct regcache_ops {
const char *name;
enum regcache_type type;
@@ -322,6 +333,7 @@ static inline unsigned int regcache_get_index_by_order(const struct regmap *map,
}
struct regmap_ram_data {
+ unsigned int base_reg;
unsigned int *vals; /* Allocatd by caller */
bool *read;
bool *written;
diff --git a/drivers/base/regmap/regcache-maple.c b/drivers/base/regmap/regcache-maple.c
index e46fe5c32b7f..16e488da5f23 100644
--- a/drivers/base/regmap/regcache-maple.c
+++ b/drivers/base/regmap/regcache-maple.c
@@ -312,16 +312,21 @@ static void regcache_maple_exit(struct regmap *map)
struct maple_tree *mt = map->cache;
MA_STATE(mas, mt, 0, UINT_MAX);
unsigned int *entry;
+ unsigned long flags;
/* if we've already been called then just return */
if (!mt)
return;
- mas_lock(&mas);
+ /*
+ * Register access may take the Maple lock from hard IRQ context, so
+ * preserve its IRQ-safe lockdep state during teardown.
+ */
+ mas_lock_irqsave(&mas, flags);
mas_for_each(&mas, entry, UINT_MAX)
kfree(entry);
__mt_destroy(mt);
- mas_unlock(&mas);
+ mas_unlock_irqrestore(&mas, flags);
kfree(mt);
map->cache = NULL;
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c
index 520d5f8ba3cd..e2feed6dbecf 100644
--- a/drivers/base/regmap/regcache-rbtree.c
+++ b/drivers/base/regmap/regcache-rbtree.c
@@ -141,7 +141,7 @@ static int rbtree_show(struct seq_file *s, void *ignored)
int registers = 0;
int this_registers, average;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
mem_size = sizeof(*rbtree_ctx);
@@ -168,8 +168,6 @@ static int rbtree_show(struct seq_file *s, void *ignored)
seq_printf(s, "%d nodes, %d registers, average %d registers, used %zu bytes\n",
nodes, registers, average, mem_size);
- map->unlock(map->lock_arg);
-
return 0;
}
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 0d58d900a2ca..774e9c9bf3c4 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -22,6 +22,8 @@ static const struct regcache_ops *cache_types[] = {
&regcache_flat_ops,
};
+static int regcache_default_sync(struct regmap *map, unsigned int min, unsigned int max);
+
static int regcache_defaults_cmp(const void *a, const void *b)
{
const struct reg_default *x = a;
@@ -56,12 +58,15 @@ static int regcache_count_cacheable_registers(struct regmap *map)
return count;
}
-static int regcache_hw_init(struct regmap *map)
+static int regcache_hw_init(struct regmap *map, int count)
{
int ret;
unsigned int reg, val;
void *tmp_buf;
+ if (!count)
+ return 0;
+
if (!map->reg_defaults_raw) {
bool cache_bypass = map->cache_bypass;
dev_dbg(map->dev, "No cache defaults, reading back from HW\n");
@@ -121,6 +126,29 @@ static void regcache_hw_exit(struct regmap *map)
kfree(map->reg_defaults_raw);
}
+static int regcache_locked_op(struct regmap *map,
+ int (*op)(struct regmap *map),
+ const char *action)
+{
+ if (!op)
+ return 0;
+
+ dev_dbg(map->dev, "%s %s cache\n", action, map->cache_ops->name);
+ guard(regmap)(map);
+ return op(map);
+}
+
+static int __regcache_sync(struct regmap *map, unsigned int min, unsigned int max)
+{
+ if (!map->cache_dirty)
+ return 0;
+
+ if (map->cache_ops->sync)
+ return map->cache_ops->sync(map, min, max);
+
+ return regcache_default_sync(map, min, max);
+}
+
int regcache_init(struct regmap *map, const struct regmap_config *config)
{
bool sort_defaults = false;
@@ -139,15 +167,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
return 0;
}
- if (config->reg_defaults && !config->num_reg_defaults) {
- dev_err(map->dev,
- "Register defaults are set without the number!\n");
- return -EINVAL;
- }
-
- if (config->num_reg_defaults && !config->reg_defaults) {
- dev_err(map->dev,
- "Register defaults number are set without the reg!\n");
+ if (!!config->reg_defaults != !!config->num_reg_defaults) {
+ dev_err(map->dev, "reg_defaults and num_reg_defaults must both be specified\n");
return -EINVAL;
}
@@ -180,9 +201,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
map->cache = NULL;
map->cache_ops = cache_types[i];
- if (!map->cache_ops->read ||
- !map->cache_ops->write ||
- !map->cache_ops->name)
+ if (!map->cache_ops->read || !map->cache_ops->write || !map->cache_ops->name)
return -EINVAL;
/* We still need to ensure that the reg_defaults
@@ -225,9 +244,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
if (map->cache_ops->init) {
dev_dbg(map->dev, "Initializing %s cache\n",
map->cache_ops->name);
- map->lock(map->lock_arg);
ret = map->cache_ops->init(map);
- map->unlock(map->lock_arg);
if (ret)
goto err_free_reg_defaults;
}
@@ -237,21 +254,17 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
* we cope with this by reading back the HW registers and
* crafting the cache defaults by hand.
*/
- if (count) {
- ret = regcache_hw_init(map);
- if (ret)
- goto err_exit;
- }
+ ret = regcache_hw_init(map, count);
+ if (ret)
+ goto err_exit;
+
+ if (!map->num_reg_defaults && !map->reg_default_cb)
+ return 0;
+
+ ret = regcache_locked_op(map, map->cache_ops->populate, "Populating");
+ if (ret)
+ goto err_free;
- if (map->cache_ops->populate &&
- (map->num_reg_defaults || map->reg_default_cb)) {
- dev_dbg(map->dev, "Populating %s cache\n", map->cache_ops->name);
- map->lock(map->lock_arg);
- ret = map->cache_ops->populate(map);
- map->unlock(map->lock_arg);
- if (ret)
- goto err_free;
- }
return 0;
err_free:
@@ -259,9 +272,7 @@ err_free:
err_exit:
if (map->cache_ops->exit) {
dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name);
- map->lock(map->lock_arg);
map->cache_ops->exit(map);
- map->unlock(map->lock_arg);
}
err_free_reg_defaults:
kfree(map->reg_defaults);
@@ -277,16 +288,14 @@ void regcache_exit(struct regmap *map)
BUG_ON(!map->cache_ops);
regcache_hw_exit(map);
-
if (map->cache_ops->exit) {
dev_dbg(map->dev, "Destroying %s cache\n",
map->cache_ops->name);
- map->lock(map->lock_arg);
map->cache_ops->exit(map);
- map->unlock(map->lock_arg);
}
kfree(map->reg_defaults);
+ map->reg_defaults = NULL;
}
/**
@@ -461,11 +470,7 @@ int regcache_sync(struct regmap *map)
}
map->cache_bypass = false;
- if (map->cache_ops->sync)
- sync_ret = map->cache_ops->sync(map, 0, map->max_register);
- else
- sync_ret = regcache_default_sync(map, 0, map->max_register);
-
+ sync_ret = __regcache_sync(map, 0, map->max_register);
if (sync_ret == 0)
map->cache_dirty = false;
@@ -545,17 +550,10 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
trace_regcache_sync(map, name, "start region");
- if (!map->cache_dirty)
- goto out;
-
map->async = true;
- if (map->cache_ops->sync)
- ret = map->cache_ops->sync(map, min, max);
- else
- ret = regcache_default_sync(map, min, max);
+ ret = __regcache_sync(map, min, max);
-out:
/* Restore the bypass state */
map->cache_bypass = bypass;
map->async = false;
@@ -584,20 +582,14 @@ EXPORT_SYMBOL_GPL(regcache_sync_region);
int regcache_drop_region(struct regmap *map, unsigned int min,
unsigned int max)
{
- int ret = 0;
-
if (!map->cache_ops || !map->cache_ops->drop)
return -EINVAL;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
trace_regcache_drop_region(map, min, max);
- ret = map->cache_ops->drop(map, min, max);
-
- map->unlock(map->lock_arg);
-
- return ret;
+ return map->cache_ops->drop(map, min, max);
}
EXPORT_SYMBOL_GPL(regcache_drop_region);
@@ -615,12 +607,11 @@ EXPORT_SYMBOL_GPL(regcache_drop_region);
*/
void regcache_cache_only(struct regmap *map, bool enable)
{
- map->lock(map->lock_arg);
+ guard(regmap)(map);
WARN_ON(map->cache_type != REGCACHE_NONE &&
map->cache_bypass && enable);
map->cache_only = enable;
trace_regmap_cache_only(map, enable);
- map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_cache_only);
@@ -639,10 +630,9 @@ EXPORT_SYMBOL_GPL(regcache_cache_only);
*/
void regcache_mark_dirty(struct regmap *map)
{
- map->lock(map->lock_arg);
+ guard(regmap)(map);
map->cache_dirty = true;
map->no_sync_defaults = true;
- map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_mark_dirty);
@@ -659,11 +649,10 @@ EXPORT_SYMBOL_GPL(regcache_mark_dirty);
*/
void regcache_cache_bypass(struct regmap *map, bool enable)
{
- map->lock(map->lock_arg);
+ guard(regmap)(map);
WARN_ON(map->cache_only && enable);
map->cache_bypass = enable;
trace_regmap_cache_bypass(map, enable);
- map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
@@ -680,12 +669,10 @@ bool regcache_reg_cached(struct regmap *map, unsigned int reg)
unsigned int val;
int ret;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
ret = regcache_read(map, reg, &val);
- map->unlock(map->lock_arg);
-
return ret == 0;
}
EXPORT_SYMBOL_GPL(regcache_reg_cached);
@@ -771,8 +758,8 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg)
if (r)
return r - map->reg_defaults;
- else
- return -ENOENT;
+
+ return -ENOENT;
}
static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx)
@@ -909,7 +896,7 @@ int regcache_sync_block(struct regmap *map, void *block,
if (regmap_can_raw_write(map) && !map->use_single_write)
return regcache_sync_block_raw(map, block, cache_present,
block_base, start, end);
- else
- return regcache_sync_block_single(map, block, cache_present,
- block_base, start, end);
+
+ return regcache_sync_block_single(map, block, cache_present,
+ block_base, start, end);
}
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 18f1c60749fe..b86048c48f3f 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -38,10 +38,9 @@ static ssize_t regmap_name_read_file(struct file *file,
{
struct regmap *map = file->private_data;
const char *name = "nodev";
+ char *buf __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL);
int ret;
- char *buf;
- buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -49,14 +48,10 @@ static ssize_t regmap_name_read_file(struct file *file,
name = map->dev->driver->name;
ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
- if (ret >= PAGE_SIZE) {
- kfree(buf);
+ if (ret >= PAGE_SIZE)
return ret;
- }
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
- return ret;
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
}
static const struct file_operations regmap_name_fops = {
@@ -112,7 +107,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
* If we don't have a cache build one so we don't have to do a
* linear scan each time.
*/
- mutex_lock(&map->cache_lock);
+ guard(mutex)(&map->cache_lock);
i = base;
if (list_empty(&map->debugfs_off_cache)) {
for (; i <= map->max_register; i += map->reg_stride) {
@@ -134,7 +129,6 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
c = kzalloc_obj(*c);
if (!c) {
regmap_debugfs_free_dump_cache(map);
- mutex_unlock(&map->cache_lock);
return base;
}
c->min = p;
@@ -167,14 +161,12 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
fpos_offset = from - c->min;
reg_offset = fpos_offset / map->debugfs_tot_len;
*pos = c->min + (reg_offset * map->debugfs_tot_len);
- mutex_unlock(&map->cache_lock);
return c->base_reg + (reg_offset * map->reg_stride);
}
*pos = c->max;
ret = c->max_reg;
}
- mutex_unlock(&map->cache_lock);
return ret;
}
@@ -196,10 +188,10 @@ static int regmap_next_readable_reg(struct regmap *map, int reg)
struct regmap_debugfs_off_cache *c;
int ret = -EINVAL;
- if (regmap_printable(map, reg + map->reg_stride)) {
- ret = reg + map->reg_stride;
- } else {
- mutex_lock(&map->cache_lock);
+ if (regmap_printable(map, reg + map->reg_stride))
+ return reg + map->reg_stride;
+
+ scoped_guard(mutex, &map->cache_lock) {
list_for_each_entry(c, &map->debugfs_off_cache, list) {
if (reg > c->max_reg)
continue;
@@ -208,8 +200,8 @@ static int regmap_next_readable_reg(struct regmap *map, int reg)
break;
}
}
- mutex_unlock(&map->cache_lock);
}
+
return ret;
}
@@ -366,8 +358,6 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
struct regmap_debugfs_off_cache *c;
loff_t p = 0;
size_t buf_pos = 0;
- char *buf;
- char *entry;
int ret;
unsigned int entry_len;
@@ -377,15 +367,13 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
if (count > (PAGE_SIZE << MAX_PAGE_ORDER))
count = PAGE_SIZE << MAX_PAGE_ORDER;
- buf = kmalloc(count, GFP_KERNEL);
+ char *buf __free(kfree) = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- entry = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!entry) {
- kfree(buf);
+ char *entry __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!entry)
return -ENOMEM;
- }
/* While we are at it, build the register dump cache
* now so the read() operation on the `registers' file
@@ -398,31 +386,27 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
/* Reset file pointer as the fixed-format of the `registers'
* file is not compatible with the `range' file */
p = 0;
- mutex_lock(&map->cache_lock);
- list_for_each_entry(c, &map->debugfs_off_cache, list) {
- entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n",
- c->base_reg, c->max_reg);
- if (p >= *ppos) {
- if (buf_pos + entry_len > count)
- break;
- memcpy(buf + buf_pos, entry, entry_len);
- buf_pos += entry_len;
+ scoped_guard(mutex, &map->cache_lock) {
+ list_for_each_entry(c, &map->debugfs_off_cache, list) {
+ entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n",
+ c->base_reg, c->max_reg);
+ if (p >= *ppos) {
+ if (buf_pos + entry_len > count)
+ break;
+ memcpy(buf + buf_pos, entry, entry_len);
+ buf_pos += entry_len;
+ }
+ p += entry_len;
}
- p += entry_len;
}
- mutex_unlock(&map->cache_lock);
- kfree(entry);
ret = buf_pos;
- if (copy_to_user(user_buf, buf, buf_pos)) {
- ret = -EFAULT;
- goto out_buf;
- }
+ if (copy_to_user(user_buf, buf, buf_pos))
+ return -EFAULT;
*ppos += buf_pos;
-out_buf:
- kfree(buf);
+
return ret;
}
@@ -471,18 +455,16 @@ static ssize_t regmap_cache_only_write_file(struct file *file,
if (err)
return count;
- map->lock(map->lock_arg);
-
- if (new_val && !map->cache_only) {
- dev_warn(map->dev, "debugfs cache_only=Y forced\n");
- add_taint(TAINT_USER, LOCKDEP_STILL_OK);
- } else if (!new_val && map->cache_only) {
- dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n");
- require_sync = true;
+ scoped_guard(regmap, map) {
+ if (new_val && !map->cache_only) {
+ dev_warn(map->dev, "debugfs cache_only=Y forced\n");
+ add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+ } else if (!new_val && map->cache_only) {
+ dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n");
+ require_sync = true;
+ }
+ map->cache_only = new_val;
}
- map->cache_only = new_val;
-
- map->unlock(map->lock_arg);
if (require_sync) {
err = regcache_sync(map);
@@ -513,7 +495,7 @@ static ssize_t regmap_cache_bypass_write_file(struct file *file,
if (err)
return count;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
if (new_val && !map->cache_bypass) {
dev_warn(map->dev, "debugfs cache_bypass=Y forced\n");
@@ -523,8 +505,6 @@ static ssize_t regmap_cache_bypass_write_file(struct file *file,
}
map->cache_bypass = new_val;
- map->unlock(map->lock_arg);
-
return count;
}
@@ -561,9 +541,8 @@ void regmap_debugfs_init(struct regmap *map)
if (!node)
return;
node->map = map;
- mutex_lock(&regmap_debugfs_early_lock);
- list_add(&node->link, &regmap_debugfs_early_list);
- mutex_unlock(&regmap_debugfs_early_lock);
+ scoped_guard(mutex, &regmap_debugfs_early_lock)
+ list_add(&node->link, &regmap_debugfs_early_list);
return;
}
@@ -662,29 +641,30 @@ void regmap_debugfs_init(struct regmap *map)
void regmap_debugfs_exit(struct regmap *map)
{
+ struct regmap_debugfs_node *node, *tmp;
+
if (map->debugfs) {
debugfs_remove_recursive(map->debugfs);
- mutex_lock(&map->cache_lock);
- regmap_debugfs_free_dump_cache(map);
- mutex_unlock(&map->cache_lock);
+ map->debugfs = NULL;
+ scoped_guard(mutex, &map->cache_lock)
+ regmap_debugfs_free_dump_cache(map);
if (map->debugfs_dummy_id >= 0) {
ida_free(&dummy_ida, map->debugfs_dummy_id);
map->debugfs_dummy_id = -1;
}
kfree(map->debugfs_name);
map->debugfs_name = NULL;
- } else {
- struct regmap_debugfs_node *node, *tmp;
- mutex_lock(&regmap_debugfs_early_lock);
- list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list,
- link) {
+ return;
+ }
+
+ scoped_guard(mutex, &regmap_debugfs_early_lock) {
+ list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) {
if (node->map == map) {
list_del(&node->link);
kfree(node);
}
}
- mutex_unlock(&regmap_debugfs_early_lock);
}
}
@@ -694,11 +674,10 @@ void regmap_debugfs_initcall(void)
regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
- mutex_lock(&regmap_debugfs_early_lock);
+ guard(mutex)(&regmap_debugfs_early_lock);
list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) {
regmap_debugfs_init(node->map);
list_del(&node->link);
kfree(node);
}
- mutex_unlock(&regmap_debugfs_early_lock);
}
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 99b55b1053ee..715eb9e7aa2a 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -963,7 +963,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
return 0;
err_domain:
- /* Should really dispose of the domain but... */
+ irq_domain_remove(d->domain);
err_mutex:
mutex_destroy(&d->lock);
lockdep_unregister_key(&d->lock_key);
diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c
index 2999d9c185d5..c78772b660d6 100644
--- a/drivers/base/regmap/regmap-kunit.c
+++ b/drivers/base/regmap/regmap-kunit.c
@@ -11,6 +11,8 @@
#define BLOCK_TEST_SIZE 12
+#define HIGH_BIT_REG ((UINT_MAX / 2) - 3)
+
KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_action, regmap_exit, struct regmap *);
struct regmap_test_priv {
@@ -23,6 +25,7 @@ struct regmap_test_param {
enum regcache_type cache;
enum regmap_endian val_endian;
+ unsigned int base_reg;
unsigned int from_reg;
bool fast_io;
};
@@ -204,7 +207,8 @@ static struct regmap *gen_regmap(struct kunit *test,
config->max_register += (BLOCK_TEST_SIZE * config->reg_stride);
}
- size = array_size(config->max_register + 1, sizeof(*buf));
+ size = array_size(config->max_register - param->base_reg + 1,
+ sizeof(*buf));
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
@@ -215,6 +219,7 @@ static struct regmap *gen_regmap(struct kunit *test,
if (!(*data))
goto out_free;
(*data)->vals = buf;
+ (*data)->base_reg = param->base_reg;
if (config->num_reg_defaults) {
defaults = kunit_kcalloc(test,
@@ -227,8 +232,8 @@ static struct regmap *gen_regmap(struct kunit *test,
config->reg_defaults = defaults;
for (i = 0; i < config->num_reg_defaults; i++) {
- defaults[i].reg = param->from_reg + (i * config->reg_stride);
- defaults[i].def = buf[param->from_reg + (i * config->reg_stride)];
+ defaults[i].reg = param->from_reg - param->base_reg + (i * config->reg_stride);
+ defaults[i].def = buf[param->from_reg - param->base_reg + (i * config->reg_stride)];
}
}
@@ -1648,6 +1653,92 @@ static void cache_write_zero(struct kunit *test)
KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, 1));
}
+static const struct regmap_test_param high_bit_cache_types_list[] = {
+ { .cache = REGCACHE_RBTREE, .from_reg = HIGH_BIT_REG,
+ .base_reg = HIGH_BIT_REG },
+ { .cache = REGCACHE_MAPLE, .from_reg = HIGH_BIT_REG,
+ .base_reg = HIGH_BIT_REG },
+};
+
+KUNIT_ARRAY_PARAM(high_bit_cache_types, high_bit_cache_types_list, param_to_desc);
+
+/*
+ * Dynamically allocated cache types should support registers with the
+ * high bit set, the backend will return an error for any operation on
+ * a register below the expected window.
+ */
+static void cache_high_bit_reg(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap_ram_data *data;
+ struct regmap_config config;
+ struct regmap *map;
+ unsigned int val[BLOCK_TEST_SIZE], rval;
+ int i;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* No defaults so no registers cached */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_FALSE(test, regcache_reg_cached(map, param->from_reg + i));
+
+ /* Writes should reach the device */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + i,
+ val[i]));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ KUNIT_EXPECT_TRUE(test, data->written[i]);
+ KUNIT_EXPECT_EQ(test, val[i], data->vals[i]);
+ }
+
+ /* Reads should be satisfied from the cache */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ KUNIT_EXPECT_TRUE(test, regcache_reg_cached(map, param->from_reg + i));
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i,
+ &rval));
+ KUNIT_EXPECT_EQ(test, val[i], rval);
+ KUNIT_EXPECT_FALSE(test, data->read[i]);
+ }
+
+ /* Trash the data on the device then resync */
+ regcache_mark_dirty(map);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ data->vals[i] = 0;
+ data->written[i] = false;
+ }
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+
+ /* Did we just write the correct data out? */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ KUNIT_EXPECT_TRUE(test, data->written[i]);
+ KUNIT_EXPECT_EQ(test, val[i], data->vals[i]);
+ }
+
+ /* Drop some registers */
+ KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, param->from_reg + 3,
+ param->from_reg + 5));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, i < 3 || i > 5,
+ regcache_reg_cached(map, param->from_reg + i));
+
+ /* Reread and check only the dropped registers hit the device */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->read[i] = false;
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i,
+ &rval));
+ KUNIT_EXPECT_EQ(test, val[i], rval);
+ KUNIT_EXPECT_EQ(test, i >= 3 && i <= 5, data->read[i]);
+ }
+}
+
/* Check that caching the window register works with sync */
static void cache_range_window_reg(struct kunit *test)
{
@@ -2168,6 +2259,7 @@ static struct kunit_case regmap_test_cases[] = {
KUNIT_CASE_PARAM(cache_drop_all_and_sync_has_defaults, sparse_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_present, sparse_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_write_zero, sparse_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_high_bit_reg, high_bit_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_range_window_reg, real_cache_types_only_gen_params),
KUNIT_CASE_PARAM(raw_read_defaults_single, raw_test_types_gen_params),
diff --git a/drivers/base/regmap/regmap-ram.c b/drivers/base/regmap/regmap-ram.c
index c7356b0d8c83..a666bfb4d667 100644
--- a/drivers/base/regmap/regmap-ram.c
+++ b/drivers/base/regmap/regmap-ram.c
@@ -20,8 +20,11 @@ static int regmap_ram_write(void *context, unsigned int reg, unsigned int val)
{
struct regmap_ram_data *data = context;
- data->vals[reg] = val;
- data->written[reg] = true;
+ if (reg < data->base_reg)
+ return -EINVAL;
+
+ data->vals[reg - data->base_reg] = val;
+ data->written[reg - data->base_reg] = true;
return 0;
}
@@ -30,8 +33,11 @@ static int regmap_ram_read(void *context, unsigned int reg, unsigned int *val)
{
struct regmap_ram_data *data = context;
- *val = data->vals[reg];
- data->read[reg] = true;
+ if (reg < data->base_reg)
+ return -EINVAL;
+
+ *val = data->vals[reg - data->base_reg];
+ data->read[reg - data->base_reg] = true;
return 0;
}
@@ -60,17 +66,25 @@ struct regmap *__regmap_init_ram(struct device *dev,
const char *lock_name)
{
struct regmap *map;
+ unsigned int num_regs;
if (!config->max_register) {
pr_crit("No max_register specified for RAM regmap\n");
return ERR_PTR(-EINVAL);
}
- data->read = kzalloc_objs(bool, config->max_register + 1);
+ if (data->base_reg > config->max_register) {
+ pr_crit("base_reg above max_register for RAM regmap\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ num_regs = config->max_register - data->base_reg + 1;
+
+ data->read = kzalloc_objs(bool, num_regs);
if (!data->read)
return ERR_PTR(-ENOMEM);
- data->written = kzalloc_objs(bool, config->max_register + 1);
+ data->written = kzalloc_objs(bool, num_regs);
if (!data->written) {
kfree(data->read);
return ERR_PTR(-ENOMEM);
diff --git a/drivers/base/regmap/regmap-raw-ram.c b/drivers/base/regmap/regmap-raw-ram.c
index 60d6e95cdd1b..c63c464549f9 100644
--- a/drivers/base/regmap/regmap-raw-ram.c
+++ b/drivers/base/regmap/regmap-raw-ram.c
@@ -16,14 +16,17 @@
#include "internal.h"
-static unsigned int decode_reg(enum regmap_endian endian, const void *reg)
+static unsigned int decode_reg(struct regmap_ram_data *data, const void *reg)
{
const u16 *r = reg;
+ unsigned int decode;
- if (endian == REGMAP_ENDIAN_BIG)
- return be16_to_cpu(*r);
+ if (data->reg_endian == REGMAP_ENDIAN_BIG)
+ decode = be16_to_cpu(*r);
else
- return le16_to_cpu(*r);
+ decode = le16_to_cpu(*r);
+
+ return decode - data->base_reg;
}
static int regmap_raw_ram_gather_write(void *context,
@@ -40,7 +43,7 @@ static int regmap_raw_ram_gather_write(void *context,
if (val_len % 2)
return -EINVAL;
- r = decode_reg(data->reg_endian, reg);
+ r = decode_reg(data, reg);
if (data->noinc_reg && data->noinc_reg(data, r)) {
memcpy(&our_buf[r], val + val_len - 2, 2);
data->written[r] = true;
@@ -74,7 +77,7 @@ static int regmap_raw_ram_read(void *context,
if (val_len % 2)
return -EINVAL;
- r = decode_reg(data->reg_endian, reg);
+ r = decode_reg(data, reg);
if (data->noinc_reg && data->noinc_reg(data, r)) {
for (i = 0; i < val_len; i += 2)
memcpy(val + i, &our_buf[r], 2);
@@ -114,6 +117,7 @@ struct regmap *__regmap_init_raw_ram(struct device *dev,
const char *lock_name)
{
struct regmap *map;
+ unsigned int regs;
if (config->reg_bits != 16)
return ERR_PTR(-EINVAL);
@@ -123,11 +127,12 @@ struct regmap *__regmap_init_raw_ram(struct device *dev,
return ERR_PTR(-EINVAL);
}
- data->read = kzalloc_objs(bool, config->max_register + 1);
+ regs = config->max_register - data->base_reg + 1;
+ data->read = kzalloc_objs(bool, regs);
if (!data->read)
return ERR_PTR(-ENOMEM);
- data->written = kzalloc_objs(bool, config->max_register + 1);
+ data->written = kzalloc_objs(bool, regs);
if (!data->written)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index e6e022b02637..83f1f5942135 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -115,9 +115,8 @@ bool regmap_cached(struct regmap *map, unsigned int reg)
if (map->max_register_is_set && reg > map->max_register)
return false;
- map->lock(map->lock_arg);
- ret = regcache_read(map, reg, &val);
- map->unlock(map->lock_arg);
+ scoped_guard(regmap, map)
+ ret = regcache_read(map, reg, &val);
if (ret)
return false;
@@ -1422,8 +1421,8 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
{
int ret;
- regcache_exit(map);
regmap_debugfs_exit(map);
+ regcache_exit(map);
map->max_register = config->max_register;
map->max_register_is_set = map->max_register ?: config->max_register_is_0;
@@ -1440,12 +1439,16 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
if (ret)
return ret;
- regmap_debugfs_init(map);
-
map->cache_bypass = false;
map->cache_only = false;
- return regcache_init(map, config);
+ ret = regcache_init(map, config);
+ if (ret)
+ return ret;
+
+ regmap_debugfs_init(map);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(regmap_reinit_cache);
@@ -1459,9 +1462,8 @@ void regmap_exit(struct regmap *map)
struct regmap_async *async;
regmap_detach_dev(map->dev, map);
- regcache_exit(map);
-
regmap_debugfs_exit(map);
+ regcache_exit(map);
regmap_range_exit(map);
if (map->bus && map->bus->free_context)
map->bus->free_context(map->bus_context);
@@ -1977,18 +1979,12 @@ int _regmap_write(struct regmap *map, unsigned int reg,
*/
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
{
- int ret;
-
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
- ret = _regmap_write(map, reg, val);
-
- map->unlock(map->lock_arg);
-
- return ret;
+ return _regmap_write(map, reg, val);
}
EXPORT_SYMBOL_GPL(regmap_write);
@@ -2009,7 +2005,7 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
map->async = true;
@@ -2017,8 +2013,6 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
map->async = false;
- map->unlock(map->lock_arg);
-
return ret;
}
EXPORT_SYMBOL_GPL(regmap_write_async);
@@ -2080,20 +2074,14 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
int regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len)
{
- int ret;
-
if (!regmap_can_raw_write(map))
return -EINVAL;
if (val_len % map->format.val_bytes)
return -EINVAL;
- map->lock(map->lock_arg);
-
- ret = _regmap_raw_write(map, reg, val, val_len, false);
-
- map->unlock(map->lock_arg);
+ guard(regmap)(map);
- return ret;
+ return _regmap_raw_write(map, reg, val, val_len, false);
}
EXPORT_SYMBOL_GPL(regmap_raw_write);
@@ -2211,21 +2199,17 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg,
if (val_len == 0)
return -EINVAL;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
- if (!regmap_volatile(map, reg) || !regmap_writeable_noinc(map, reg)) {
- ret = -EINVAL;
- goto out_unlock;
- }
+ if (!regmap_volatile(map, reg) || !regmap_writeable_noinc(map, reg))
+ return -EINVAL;
/*
* Use the accelerated operation if we can. The val drops the const
* typing in order to facilitate code reuse in regmap_noinc_readwrite().
*/
- if (map->bus->reg_noinc_write) {
- ret = regmap_noinc_readwrite(map, reg, (void *)val, val_len, true);
- goto out_unlock;
- }
+ if (map->bus->reg_noinc_write)
+ return regmap_noinc_readwrite(map, reg, (void *)val, val_len, true);
while (val_len) {
if (map->max_raw_write && map->max_raw_write < val_len)
@@ -2234,14 +2218,12 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg,
write_len = val_len;
ret = _regmap_raw_write(map, reg, val, write_len, true);
if (ret)
- goto out_unlock;
+ return ret;
val = ((u8 *)val) + write_len;
val_len -= write_len;
}
-out_unlock:
- map->unlock(map->lock_arg);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(regmap_noinc_write);
@@ -2357,7 +2339,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
* single write operations.
*/
if (!map->write || !map->format.parse_inplace) {
- map->lock(map->lock_arg);
+ guard(regmap)(map);
+
for (i = 0; i < val_count; i++) {
unsigned int ival;
@@ -2372,18 +2355,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
ival = *(u32 *)(val + (i * val_bytes));
break;
default:
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
ret = _regmap_write(map,
reg + regmap_get_offset(map, i),
ival);
if (ret != 0)
- goto out;
+ return ret;
}
-out:
- map->unlock(map->lock_arg);
} else {
void *wval;
@@ -2653,15 +2633,9 @@ static int _regmap_multi_reg_write(struct regmap *map,
int regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs,
int num_regs)
{
- int ret;
-
- map->lock(map->lock_arg);
+ guard(regmap)(map);
- ret = _regmap_multi_reg_write(map, regs, num_regs);
-
- map->unlock(map->lock_arg);
-
- return ret;
+ return _regmap_multi_reg_write(map, regs, num_regs);
}
EXPORT_SYMBOL_GPL(regmap_multi_reg_write);
@@ -2690,7 +2664,7 @@ int regmap_multi_reg_write_bypassed(struct regmap *map,
int ret;
bool bypass;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
bypass = map->cache_bypass;
map->cache_bypass = true;
@@ -2699,8 +2673,6 @@ int regmap_multi_reg_write_bypassed(struct regmap *map,
map->cache_bypass = bypass;
- map->unlock(map->lock_arg);
-
return ret;
}
EXPORT_SYMBOL_GPL(regmap_multi_reg_write_bypassed);
@@ -2722,7 +2694,7 @@ EXPORT_SYMBOL_GPL(regmap_multi_reg_write_bypassed);
* If supported by the underlying bus the write will be scheduled
* asynchronously, helping maximise I/O speed on higher speed buses
* like SPI. regmap_async_complete() can be called to ensure that all
- * asynchrnous writes have been completed.
+ * asynchronous writes have been completed.
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
@@ -2737,7 +2709,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
map->async = true;
@@ -2745,8 +2717,6 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
map->async = false;
- map->unlock(map->lock_arg);
-
return ret;
}
EXPORT_SYMBOL_GPL(regmap_raw_write_async);
@@ -2863,18 +2833,12 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
*/
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
- int ret;
-
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
- map->lock(map->lock_arg);
-
- ret = _regmap_read(map, reg, val);
-
- map->unlock(map->lock_arg);
+ guard(regmap)(map);
- return ret;
+ return _regmap_read(map, reg, val);
}
EXPORT_SYMBOL_GPL(regmap_read);
@@ -2897,7 +2861,7 @@ int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
bypass = map->cache_bypass;
cache_only = map->cache_only;
@@ -2909,8 +2873,6 @@ int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val
map->cache_bypass = bypass;
map->cache_only = cache_only;
- map->unlock(map->lock_arg);
-
return ret;
}
EXPORT_SYMBOL_GPL(regmap_read_bypassed);
@@ -2941,22 +2903,18 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
if (val_count == 0)
return -EINVAL;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass ||
map->cache_type == REGCACHE_NONE) {
size_t chunk_count, chunk_bytes;
size_t chunk_regs = val_count;
- if (!map->cache_bypass && map->cache_only) {
- ret = -EBUSY;
- goto out;
- }
+ if (!map->cache_bypass && map->cache_only)
+ return -EBUSY;
- if (!map->read) {
- ret = -ENOTSUPP;
- goto out;
- }
+ if (!map->read)
+ return -ENOTSUPP;
if (map->use_single_read)
chunk_regs = 1;
@@ -2970,7 +2928,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
for (i = 0; i < chunk_count; i++) {
ret = _regmap_raw_read(map, reg, val, chunk_bytes, false);
if (ret != 0)
- goto out;
+ return ret;
reg += regmap_get_offset(map, chunk_regs);
val += chunk_bytes;
@@ -2981,7 +2939,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
if (val_len) {
ret = _regmap_raw_read(map, reg, val, val_len, false);
if (ret != 0)
- goto out;
+ return ret;
}
} else {
/* Otherwise go word by word for the cache; should be low
@@ -2991,16 +2949,13 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
ret = _regmap_read(map, reg + regmap_get_offset(map, i),
&v);
if (ret != 0)
- goto out;
+ return ret;
map->format.format_val(val + (i * val_bytes), v, 0);
}
}
- out:
- map->unlock(map->lock_arg);
-
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(regmap_raw_read);
@@ -3041,12 +2996,10 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
if (val_len == 0)
return -EINVAL;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
- if (!regmap_volatile(map, reg) || !regmap_readable_noinc(map, reg)) {
- ret = -EINVAL;
- goto out_unlock;
- }
+ if (!regmap_volatile(map, reg) || !regmap_readable_noinc(map, reg))
+ return -EINVAL;
/*
* We have not defined the FIFO semantics for cache, as the
@@ -3054,16 +3007,12 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
* written value? Just avoid this by always reading the FIFO
* even when using cache. Cache only will not work.
*/
- if (!map->cache_bypass && map->cache_only) {
- ret = -EBUSY;
- goto out_unlock;
- }
+ if (!map->cache_bypass && map->cache_only)
+ return -EBUSY;
/* Use the accelerated operation if we can */
- if (map->bus->reg_noinc_read) {
- ret = regmap_noinc_readwrite(map, reg, val, val_len, false);
- goto out_unlock;
- }
+ if (map->bus->reg_noinc_read)
+ return regmap_noinc_readwrite(map, reg, val, val_len, false);
while (val_len) {
if (map->max_raw_read && map->max_raw_read < val_len)
@@ -3072,14 +3021,12 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
read_len = val_len;
ret = _regmap_raw_read(map, reg, val, read_len, true);
if (ret)
- goto out_unlock;
+ return ret;
val = ((u8 *)val) + read_len;
val_len -= read_len;
}
-out_unlock:
- map->unlock(map->lock_arg);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(regmap_noinc_read);
@@ -3149,22 +3096,20 @@ static int _regmap_bulk_read(struct regmap *map, unsigned int reg,
u8 *u8 = val;
int ret, i;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
for (i = 0; i < val_count; i++) {
unsigned int ival;
if (regs) {
- if (!IS_ALIGNED(regs[i], map->reg_stride)) {
- ret = -EINVAL;
- goto out;
- }
+ if (!IS_ALIGNED(regs[i], map->reg_stride))
+ return -EINVAL;
ret = _regmap_read(map, regs[i], &ival);
} else {
ret = _regmap_read(map, reg + regmap_get_offset(map, i), &ival);
}
if (ret != 0)
- goto out;
+ return ret;
switch (map->format.val_bytes) {
case 4:
@@ -3177,13 +3122,11 @@ static int _regmap_bulk_read(struct regmap *map, unsigned int reg,
u8[i] = ival;
break;
default:
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
}
-out:
- map->unlock(map->lock_arg);
- return ret;
+
+ return 0;
}
/**
@@ -3310,7 +3253,7 @@ int regmap_update_bits_base(struct regmap *map, unsigned int reg,
{
int ret;
- map->lock(map->lock_arg);
+ guard(regmap)(map);
map->async = async;
@@ -3318,8 +3261,6 @@ int regmap_update_bits_base(struct regmap *map, unsigned int reg,
map->async = false;
- map->unlock(map->lock_arg);
-
return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits_base);
@@ -3441,30 +3382,25 @@ int regmap_register_patch(struct regmap *map, const struct reg_sequence *regs,
num_regs))
return 0;
- p = krealloc(map->patch,
- sizeof(struct reg_sequence) * (map->patch_regs + num_regs),
- GFP_KERNEL);
- if (p) {
- memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs));
- map->patch = p;
- map->patch_regs += num_regs;
- } else {
+ p = krealloc_array(map->patch, map->patch_regs + num_regs, sizeof(*p), GFP_KERNEL);
+ if (!p)
return -ENOMEM;
- }
- map->lock(map->lock_arg);
+ memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs));
+ map->patch = p;
+ map->patch_regs += num_regs;
- bypass = map->cache_bypass;
-
- map->cache_bypass = true;
- map->async = true;
+ scoped_guard(regmap, map) {
+ bypass = map->cache_bypass;
- ret = _regmap_multi_reg_write(map, regs, num_regs);
+ map->cache_bypass = true;
+ map->async = true;
- map->async = false;
- map->cache_bypass = bypass;
+ ret = _regmap_multi_reg_write(map, regs, num_regs);
- map->unlock(map->lock_arg);
+ map->async = false;
+ map->cache_bypass = bypass;
+ }
regmap_async_complete(map);
diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig
index 542ce07530a1..1ecf0791241a 100644
--- a/drivers/base/test/Kconfig
+++ b/drivers/base/test/Kconfig
@@ -17,7 +17,6 @@ config DM_KUNIT_TEST
config DRIVER_PE_KUNIT_TEST
tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
depends on KUNIT
- select OF
default KUNIT_ALL_TESTS
config DRIVER_SWNODE_KUNIT_TEST
diff --git a/drivers/base/test/property-entry-test.c b/drivers/base/test/property-entry-test.c
index 855e73b9b21f..89cdfc2f8498 100644
--- a/drivers/base/test/property-entry-test.c
+++ b/drivers/base/test/property-entry-test.c
@@ -523,6 +523,9 @@ static void pe_test_child_iteration(struct kunit *test)
struct fwnode_handle *child;
int error, i, num;
+ if (!IS_ENABLED(CONFIG_OF))
+ kunit_skip(test, "requires CONFIG_OF");
+
static const struct software_node node = { .name = "sw" };
static const struct software_node node1 = { .name = "sw-1", .parent = &node};
static const struct software_node node2 = { .name = "sw-2", .parent = &node};
diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c
index 351c3d3ce6a0..2bee796601ed 100644
--- a/drivers/base/transport_class.c
+++ b/drivers/base/transport_class.c
@@ -7,7 +7,7 @@
*
* The basic idea here is to allow any "device controller" (which
* would most often be a Host Bus Adapter to use the services of one
- * or more tranport classes for performing transport specific
+ * or more transport classes for performing transport specific
* services. Transport specific services are things that the generic
* command layer doesn't want to know about (speed settings, line
* condidtioning, etc), but which the user might be interested in.