Skip to content

Commit

Permalink
Merge tag 'regmap-3.9' of git://git.kernel.org/pub/scm/linux/kernel/g…
Browse files Browse the repository at this point in the history
…it/broonie/regmap

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
  ...
  • Loading branch information
Linus Torvalds committed Feb 20, 2013
2 parents 9ae46e6 + a2b37ef commit 88cff24
Show file tree
Hide file tree
Showing 12 changed files with 737 additions and 118 deletions.
2 changes: 1 addition & 1 deletion drivers/base/regmap/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
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
Expand Down
22 changes: 22 additions & 0 deletions drivers/base/regmap/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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 changes: 72 additions & 0 deletions drivers/base/regmap/regcache-flat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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 change: 1 addition & 0 deletions drivers/base/regmap/regcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
50 changes: 32 additions & 18 deletions drivers/base/regmap/regmap-debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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);
}
Expand All @@ -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;
}
WARN_ON(list_empty(&map->debugfs_off_cache));
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)
Expand All @@ -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);
Expand All @@ -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 */
Expand Down
Loading

0 comments on commit 88cff24

Please sign in to comment.