Commit 88cff241 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull regmap updates from Mark Brown:
 "Several nice new features and performance improvements here,
  especially the first:

   - Support for using the cache infrastructure without the physical
     I/O, allowing devices which don't fit the physical model regmap has
     to take advantage of the cache infrastructure, contributed by
     Andrey Smirnov.

   - Several small improvements to the support for wake capable IRQs.

   - Support for asynchronous I/O, allowing us to come much closer to
     saturating fast buses like SPI.

   - Support for simple array caches, giving higher performance for use
     with MMIO devices.

   - Restoration of the use of bulk reads for handling interrupts,
     giving a performance improvement.

   - Support for 24 bit register addresses.

   - More performance improvements for debugfs."

* tag 'regmap-3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: (24 commits)
  regmap: mmio: add register clock support
  regmap: debugfs: Factor out debugfs_tot_len calc into a function
  regmap: debugfs: Optimize seeking within blocks of registers
  regmap: debugfs: Add a `max_reg' member in struct regmap_debugfs_off_cache
  regmap: debugfs: Fix reading in register field units
  regmap: spi: Handle allocation failures gracefully
  regmap: Export regmap_async_complete()
  regmap: Export regmap_async_complete_cb
  regmap: include linux/sched.h to fix build
  regmap: spi: Support asynchronous I/O for SPI
  regmap: Add asynchronous I/O support
  regmap: Add "no-bus" option for regmap API
  regmap: regmap: avoid spurious warning in regmap_read_debugfs
  regmap: Add provisions to have user-defined write operation
  regmap: Add provisions to have user-defined read operation
  regmap: Add support for 24 bit wide register addresses
  mfd: wm5110: Mark wakes as inverted
  mfd: wm5102: Mark wakes as inverted
  regmap: irq: Support wake IRQ mask inversion
  regmap: irq: Fix sync of wake statuses to hardware
  ...
parents 9ae46e67 a2b37efc
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
obj-$(CONFIG_REGMAP) += regmap.o regcache.o
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o
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
+22 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/regmap.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/wait.h>

struct regmap;
struct regcache_ops;
@@ -25,6 +26,7 @@ struct regmap_debugfs_off_cache {
	off_t min;
	off_t max;
	unsigned int base_reg;
	unsigned int max_reg;
};

struct regmap_format {
@@ -39,6 +41,13 @@ struct regmap_format {
	unsigned int (*parse_val)(void *buf);
};

struct regmap_async {
	struct list_head list;
	struct work_struct cleanup;
	struct regmap *map;
	void *work_buf;
};

struct regmap {
	struct mutex mutex;
	spinlock_t spinlock;
@@ -53,6 +62,11 @@ struct regmap {
	void *bus_context;
	const char *name;

	spinlock_t async_lock;
	wait_queue_head_t async_waitq;
	struct list_head async_list;
	int async_ret;

#ifdef CONFIG_DEBUG_FS
	struct dentry *debugfs;
	const char *debugfs_name;
@@ -74,6 +88,11 @@ struct regmap {
	const struct regmap_access_table *volatile_table;
	const struct regmap_access_table *precious_table;

	int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
	int (*reg_write)(void *context, unsigned int reg, unsigned int val);

	bool defer_caching;

	u8 read_flag_mask;
	u8 write_flag_mask;

@@ -175,7 +194,10 @@ bool regcache_set_val(void *base, unsigned int idx,
		      unsigned int val, unsigned int word_size);
int regcache_lookup_reg(struct regmap *map, unsigned int reg);

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

extern struct regcache_ops regcache_rbtree_ops;
extern struct regcache_ops regcache_lzo_ops;
extern struct regcache_ops regcache_flat_ops;

#endif
+72 −0
Original line number Diff line number Diff line
/*
 * Register cache access API - flat caching support
 *
 * Copyright 2012 Wolfson Microelectronics plc
 *
 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/slab.h>
#include <linux/device.h>
#include <linux/seq_file.h>

#include "internal.h"

static int regcache_flat_init(struct regmap *map)
{
	int i;
	unsigned int *cache;

	map->cache = kzalloc(sizeof(unsigned int) * (map->max_register + 1),
			     GFP_KERNEL);
	if (!map->cache)
		return -ENOMEM;

	cache = map->cache;

	for (i = 0; i < map->num_reg_defaults; i++)
		cache[map->reg_defaults[i].reg] = map->reg_defaults[i].def;

	return 0;
}

static int regcache_flat_exit(struct regmap *map)
{
	kfree(map->cache);
	map->cache = NULL;

	return 0;
}

static int regcache_flat_read(struct regmap *map,
			      unsigned int reg, unsigned int *value)
{
	unsigned int *cache = map->cache;

	*value = cache[reg];

	return 0;
}

static int regcache_flat_write(struct regmap *map, unsigned int reg,
			       unsigned int value)
{
	unsigned int *cache = map->cache;

	cache[reg] = value;

	return 0;
}

struct regcache_ops regcache_flat_ops = {
	.type = REGCACHE_FLAT,
	.name = "flat",
	.init = regcache_flat_init,
	.exit = regcache_flat_exit,
	.read = regcache_flat_read,
	.write = regcache_flat_write,
};
+1 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
static const struct regcache_ops *cache_types[] = {
	&regcache_rbtree_ops,
	&regcache_lzo_ops,
	&regcache_flat_ops,
};

static int regcache_hw_init(struct regmap *map)
+32 −18
Original line number Diff line number Diff line
@@ -81,6 +81,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
	struct regmap_debugfs_off_cache *c = NULL;
	loff_t p = 0;
	unsigned int i, ret;
	unsigned int fpos_offset;
	unsigned int reg_offset;

	/*
	 * If we don't have a cache build one so we don't have to do a
@@ -93,6 +95,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
			    regmap_precious(map, i)) {
				if (c) {
					c->max = p - 1;
					fpos_offset = c->max - c->min;
					reg_offset = fpos_offset / map->debugfs_tot_len;
					c->max_reg = c->base_reg + reg_offset;
					list_add_tail(&c->list,
						      &map->debugfs_off_cache);
					c = NULL;
@@ -119,6 +124,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
	/* Close the last entry off if we didn't scan beyond it */
	if (c) {
		c->max = p - 1;
		fpos_offset = c->max - c->min;
		reg_offset = fpos_offset / map->debugfs_tot_len;
		c->max_reg = c->base_reg + reg_offset;
		list_add_tail(&c->list,
			      &map->debugfs_off_cache);
	}
@@ -128,25 +136,38 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
	 * allocate and we should never be in this code if there are
	 * no registers at all.
	 */
	if (list_empty(&map->debugfs_off_cache)) {
	WARN_ON(list_empty(&map->debugfs_off_cache));
		return base;
	}
	ret = base;

	/* Find the relevant block */
	/* Find the relevant block:offset */
	list_for_each_entry(c, &map->debugfs_off_cache, list) {
		if (from >= c->min && from <= c->max) {
			*pos = c->min;
			return c->base_reg;
			fpos_offset = from - c->min;
			reg_offset = fpos_offset / map->debugfs_tot_len;
			*pos = c->min + (reg_offset * map->debugfs_tot_len);
			return c->base_reg + reg_offset;
		}

		*pos = c->min;
		ret = c->base_reg;
		*pos = c->max;
		ret = c->max_reg;
	}

	return ret;
}

static inline void regmap_calc_tot_len(struct regmap *map,
				       void *buf, size_t count)
{
	/* Calculate the length of a fixed format  */
	if (!map->debugfs_tot_len) {
		map->debugfs_reg_len = regmap_calc_reg_len(map->max_register,
							   buf, count);
		map->debugfs_val_len = 2 * map->format.val_bytes;
		map->debugfs_tot_len = map->debugfs_reg_len +
			map->debugfs_val_len + 3;      /* : \n */
	}
}

static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
				   unsigned int to, char __user *user_buf,
				   size_t count, loff_t *ppos)
@@ -165,14 +186,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
	if (!buf)
		return -ENOMEM;

	/* Calculate the length of a fixed format  */
	if (!map->debugfs_tot_len) {
		map->debugfs_reg_len = regmap_calc_reg_len(map->max_register,
							   buf, count);
		map->debugfs_val_len = 2 * map->format.val_bytes;
		map->debugfs_tot_len = map->debugfs_reg_len +
			map->debugfs_val_len + 3;      /* : \n */
	}
	regmap_calc_tot_len(map, buf, count);

	/* Work out which register we're starting at */
	start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p);
@@ -187,7 +201,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
		/* If we're in the region the user is trying to read */
		if (p >= *ppos) {
			/* ...but not beyond it */
			if (buf_pos + 1 + map->debugfs_tot_len >= count)
			if (buf_pos + map->debugfs_tot_len > count)
				break;

			/* Format the register */
Loading