Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4fc9ed33 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull regmap updates from Mark Brown:
 "The main thing this time around has been some improvments to async
  I/O.

   - Cleaned up the async I/O support and extended it to allow single
     register writes more easily.  This is now used where possible for
     internally generated I/O, providing performance improvements for
     devices that can do async I/O.
   - An API for issuing a sequence of register writes as a single
     operation.  Some devices and buses can take advantage of this to do
     the I/O faster.
   - Addition of regmap_field APIs which help drivers for devices with
     repeated IPs or which move registers around between revisions to
     share helpers.
   - Support for SPMI buses"

* tag 'regmap-v3.13' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: add SPMI support
  regmap: debugfs: Fix a boot time crash with early regmap init
  regmap: irq: clear status when disable irq
  regmap: Only send a single buffer for async I/O if writing one register
  regmap: spi: Handle async writes of only one buffer
  regmap: new API regmap_multi_reg_write() definition
  regmap: Use async I/O during cache sync
  regmap: Use async I/O for patch application
  regmap: Fix regmap_bulk_write single-rw mutex deadlock
  regmap: Provide asynchronous write and update bits operations
  regmap: Simplify the initiation of async I/O
  regmap: Don't generate gather writes for single register raw writes
  regmap: Cache async work structures
  regmap: add helper macro to set min/max range of register
  regmap: Add regmap_fields APIs
  regmap: add regmap_field_update_bits()
parents 7e238a2e 04bc9ac1
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
# subsystems should select the appropriate symbols.

config REGMAP
	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_MMIO || REGMAP_IRQ)
	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ)
	select LZO_COMPRESS
	select LZO_DECOMPRESS
	select IRQ_DOMAIN if REGMAP_IRQ
@@ -15,6 +15,9 @@ config REGMAP_I2C
config REGMAP_SPI
	tristate

config REGMAP_SPMI
	tristate

config REGMAP_MMIO
	tristate

+1 −0
Original line number Diff line number Diff line
@@ -3,5 +3,6 @@ obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o
obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o
+6 −2
Original line number Diff line number Diff line
@@ -44,7 +44,6 @@ struct regmap_format {

struct regmap_async {
	struct list_head list;
	struct work_struct cleanup;
	struct regmap *map;
	void *work_buf;
};
@@ -64,9 +63,11 @@ struct regmap {
	void *bus_context;
	const char *name;

	bool async;
	spinlock_t async_lock;
	wait_queue_head_t async_waitq;
	struct list_head async_list;
	struct list_head async_free;
	int async_ret;

#ifdef CONFIG_DEBUG_FS
@@ -179,6 +180,9 @@ struct regmap_field {
	/* lsb */
	unsigned int shift;
	unsigned int reg;

	unsigned int id_size;
	unsigned int id_offset;
};

#ifdef CONFIG_DEBUG_FS
@@ -218,7 +222,7 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
int regcache_lookup_reg(struct regmap *map, unsigned int reg);

int _regmap_raw_write(struct regmap *map, unsigned int reg,
		      const void *val, size_t val_len, bool async);
		      const void *val, size_t val_len);

void regmap_async_complete_cb(struct regmap_async *async, int ret);

+15 −4
Original line number Diff line number Diff line
@@ -307,6 +307,8 @@ int regcache_sync(struct regmap *map)
	if (!map->cache_dirty)
		goto out;

	map->async = true;

	/* Apply any patch first */
	map->cache_bypass = 1;
	for (i = 0; i < map->patch_regs; i++) {
@@ -332,11 +334,15 @@ int regcache_sync(struct regmap *map)
		map->cache_dirty = false;

out:
	trace_regcache_sync(map->dev, name, "stop");
	/* Restore the bypass state */
	map->async = false;
	map->cache_bypass = bypass;
	map->unlock(map->lock_arg);

	regmap_async_complete(map);

	trace_regcache_sync(map->dev, name, "stop");

	return ret;
}
EXPORT_SYMBOL_GPL(regcache_sync);
@@ -375,17 +381,23 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
	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);

out:
	trace_regcache_sync(map->dev, name, "stop region");
	/* Restore the bypass state */
	map->cache_bypass = bypass;
	map->async = false;
	map->unlock(map->lock_arg);

	regmap_async_complete(map);

	trace_regcache_sync(map->dev, name, "stop region");

	return ret;
}
EXPORT_SYMBOL_GPL(regcache_sync_region);
@@ -631,8 +643,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,

	map->cache_bypass = 1;

	ret = _regmap_raw_write(map, base, *data, count * val_bytes,
				false);
	ret = _regmap_raw_write(map, base, *data, count * val_bytes);

	map->cache_bypass = 0;

+52 −5
Original line number Diff line number Diff line
@@ -15,10 +15,19 @@
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/list.h>

#include "internal.h"

struct regmap_debugfs_node {
	struct regmap *map;
	const char *name;
	struct list_head link;
};

static struct dentry *regmap_debugfs_root;
static LIST_HEAD(regmap_debugfs_early_list);
static DEFINE_MUTEX(regmap_debugfs_early_lock);

/* Calculate the length of a fixed format  */
static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size)
@@ -465,6 +474,20 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
	struct rb_node *next;
	struct regmap_range_node *range_node;

	/* If we don't have the debugfs root yet, postpone init */
	if (!regmap_debugfs_root) {
		struct regmap_debugfs_node *node;
		node = kzalloc(sizeof(*node), GFP_KERNEL);
		if (!node)
			return;
		node->map = map;
		node->name = name;
		mutex_lock(&regmap_debugfs_early_lock);
		list_add(&node->link, &regmap_debugfs_early_list);
		mutex_unlock(&regmap_debugfs_early_lock);
		return;
	}

	INIT_LIST_HEAD(&map->debugfs_off_cache);
	mutex_init(&map->cache_lock);

@@ -519,18 +542,42 @@ void regmap_debugfs_init(struct regmap *map, const char *name)

void regmap_debugfs_exit(struct regmap *map)
{
	if (map->debugfs) {
		debugfs_remove_recursive(map->debugfs);
		mutex_lock(&map->cache_lock);
		regmap_debugfs_free_dump_cache(map);
		mutex_unlock(&map->cache_lock);
		kfree(map->debugfs_name);
	} 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) {
			if (node->map == map) {
				list_del(&node->link);
				kfree(node);
			}
		}
		mutex_unlock(&regmap_debugfs_early_lock);
	}
}

void regmap_debugfs_initcall(void)
{
	struct regmap_debugfs_node *node, *tmp;

	regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
	if (!regmap_debugfs_root) {
		pr_warn("regmap: Failed to create debugfs root\n");
		return;
	}

	mutex_lock(&regmap_debugfs_early_lock);
	list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) {
		regmap_debugfs_init(node->map, node->name);
		list_del(&node->link);
		kfree(node);
	}
	mutex_unlock(&regmap_debugfs_early_lock);
}
Loading