]> Pileus Git - ~andy/linux/commitdiff
Merge tag 'regmap-3.4' into regmap-stride
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Sat, 12 May 2012 12:06:08 +0000 (13:06 +0100)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Sat, 12 May 2012 12:06:08 +0000 (13:06 +0100)
regmap: Last minute bug fix for 3.4

This is a last minute bug fix that was only just noticed since the code
path that's being exercised here is one that is fairly rarely used.  The
changelog for the change itself is extremely clear and the code itself
is obvious to inspection so should be pretty safe.

Conflicts:
drivers/base/regmap/regmap.c (overlap between the fix and stride code)

13 files changed:
drivers/base/regmap/Kconfig
drivers/base/regmap/Makefile
drivers/base/regmap/internal.h
drivers/base/regmap/regcache-lzo.c
drivers/base/regmap/regcache-rbtree.c
drivers/base/regmap/regcache.c
drivers/base/regmap/regmap-debugfs.c
drivers/base/regmap/regmap-i2c.c
drivers/base/regmap/regmap-irq.c
drivers/base/regmap/regmap-mmio.c [new file with mode: 0644]
drivers/base/regmap/regmap-spi.c
drivers/base/regmap/regmap.c
include/linux/regmap.h

index 0f6c7fb418e872e05bdbc5bf6c075e6390fb82c4..9ef0a5326f170e9f4b0b8c3a661842c2f98e2668 100644 (file)
@@ -14,5 +14,8 @@ config REGMAP_I2C
 config REGMAP_SPI
        tristate
 
+config REGMAP_MMIO
+       tristate
+
 config REGMAP_IRQ
        bool
index defd57963c84ec5d91ecb756317bb385ae1e3210..5e75d1b683e236cf8ecc35a5c6f9b28ced8d30f3 100644 (file)
@@ -3,4 +3,5 @@ obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.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_MMIO) += regmap-mmio.o
 obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o
index fcafc5b2e65113e36e496079ec2e20c93e20abb3..2eb719704885cbfe9497e0c04de7f5d088d990a6 100644 (file)
@@ -26,21 +26,29 @@ struct regmap_format {
        size_t val_bytes;
        void (*format_write)(struct regmap *map,
                             unsigned int reg, unsigned int val);
-       void (*format_reg)(void *buf, unsigned int reg);
-       void (*format_val)(void *buf, unsigned int val);
+       void (*format_reg)(void *buf, unsigned int reg, unsigned int shift);
+       void (*format_val)(void *buf, unsigned int val, unsigned int shift);
        unsigned int (*parse_val)(void *buf);
 };
 
+typedef void (*regmap_lock)(struct regmap *map);
+typedef void (*regmap_unlock)(struct regmap *map);
+
 struct regmap {
-       struct mutex lock;
+       struct mutex mutex;
+       spinlock_t spinlock;
+       regmap_lock lock;
+       regmap_unlock unlock;
 
        struct device *dev; /* Device we do I/O on */
        void *work_buf;     /* Scratch buffer used to format I/O */
        struct regmap_format format;  /* Buffer format */
        const struct regmap_bus *bus;
+       void *bus_context;
 
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs;
+       const char *debugfs_name;
 #endif
 
        unsigned int max_register;
@@ -52,6 +60,10 @@ struct regmap {
        u8 read_flag_mask;
        u8 write_flag_mask;
 
+       /* number of bits to (left) shift the reg value when formatting*/
+       int reg_shift;
+       int reg_stride;
+
        /* regcache specific members */
        const struct regcache_ops *cache_ops;
        enum regcache_type cache_type;
@@ -79,6 +91,9 @@ struct regmap {
 
        struct reg_default *patch;
        int patch_regs;
+
+       /* if set, converts bulk rw to single rw */
+       bool use_single_rw;
 };
 
 struct regcache_ops {
@@ -101,11 +116,11 @@ int _regmap_write(struct regmap *map, unsigned int reg,
 
 #ifdef CONFIG_DEBUG_FS
 extern void regmap_debugfs_initcall(void);
-extern void regmap_debugfs_init(struct regmap *map);
+extern void regmap_debugfs_init(struct regmap *map, const char *name);
 extern void regmap_debugfs_exit(struct regmap *map);
 #else
 static inline void regmap_debugfs_initcall(void) { }
-static inline void regmap_debugfs_init(struct regmap *map) { }
+static inline void regmap_debugfs_init(struct regmap *map, const char *name) { }
 static inline void regmap_debugfs_exit(struct regmap *map) { }
 #endif
 
index 483b06d4a3808adb15fe3606691723735876101f..afd6aa91a0dff572c7a01d4642bf094d98ff6fb6 100644 (file)
@@ -108,7 +108,7 @@ static int regcache_lzo_decompress_cache_block(struct regmap *map,
 static inline int regcache_lzo_get_blkindex(struct regmap *map,
                                            unsigned int reg)
 {
-       return (reg * map->cache_word_size) /
+       return ((reg / map->reg_stride) * map->cache_word_size) /
                DIV_ROUND_UP(map->cache_size_raw,
                             regcache_lzo_block_count(map));
 }
@@ -116,9 +116,10 @@ static inline int regcache_lzo_get_blkindex(struct regmap *map,
 static inline int regcache_lzo_get_blkpos(struct regmap *map,
                                          unsigned int reg)
 {
-       return reg % (DIV_ROUND_UP(map->cache_size_raw,
-                                  regcache_lzo_block_count(map)) /
-                     map->cache_word_size);
+       return (reg / map->reg_stride) %
+                   (DIV_ROUND_UP(map->cache_size_raw,
+                                 regcache_lzo_block_count(map)) /
+                    map->cache_word_size);
 }
 
 static inline int regcache_lzo_get_blksize(struct regmap *map)
@@ -322,7 +323,7 @@ static int regcache_lzo_write(struct regmap *map,
        }
 
        /* set the bit so we know we have to sync this register */
-       set_bit(reg, lzo_block->sync_bmp);
+       set_bit(reg / map->reg_stride, lzo_block->sync_bmp);
        kfree(tmp_dst);
        kfree(lzo_block->src);
        return 0;
index 92b779ee002bba20b6af60953aa2132526380c4e..e6732cf7c06eea7aedf62e383a390ef3e61c3fe2 100644 (file)
@@ -39,11 +39,12 @@ struct regcache_rbtree_ctx {
 };
 
 static inline void regcache_rbtree_get_base_top_reg(
+       struct regmap *map,
        struct regcache_rbtree_node *rbnode,
        unsigned int *base, unsigned int *top)
 {
        *base = rbnode->base_reg;
-       *top = rbnode->base_reg + rbnode->blklen - 1;
+       *top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride);
 }
 
 static unsigned int regcache_rbtree_get_register(
@@ -70,7 +71,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
 
        rbnode = rbtree_ctx->cached_rbnode;
        if (rbnode) {
-               regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
+               regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
+                                                &top_reg);
                if (reg >= base_reg && reg <= top_reg)
                        return rbnode;
        }
@@ -78,7 +80,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
        node = rbtree_ctx->root.rb_node;
        while (node) {
                rbnode = container_of(node, struct regcache_rbtree_node, node);
-               regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
+               regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
+                                                &top_reg);
                if (reg >= base_reg && reg <= top_reg) {
                        rbtree_ctx->cached_rbnode = rbnode;
                        return rbnode;
@@ -92,7 +95,7 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
        return NULL;
 }
 
-static int regcache_rbtree_insert(struct rb_root *root,
+static int regcache_rbtree_insert(struct regmap *map, struct rb_root *root,
                                  struct regcache_rbtree_node *rbnode)
 {
        struct rb_node **new, *parent;
@@ -106,7 +109,7 @@ static int regcache_rbtree_insert(struct rb_root *root,
                rbnode_tmp = container_of(*new, struct regcache_rbtree_node,
                                          node);
                /* base and top registers of the current rbnode */
-               regcache_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp,
+               regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp,
                                                 &top_reg_tmp);
                /* base register of the rbnode to be added */
                base_reg = rbnode->base_reg;
@@ -138,19 +141,20 @@ static int rbtree_show(struct seq_file *s, void *ignored)
        unsigned int base, top;
        int nodes = 0;
        int registers = 0;
-       int average;
+       int this_registers, average;
 
-       mutex_lock(&map->lock);
+       map->lock(map);
 
        for (node = rb_first(&rbtree_ctx->root); node != NULL;
             node = rb_next(node)) {
                n = container_of(node, struct regcache_rbtree_node, node);
 
-               regcache_rbtree_get_base_top_reg(n, &base, &top);
-               seq_printf(s, "%x-%x (%d)\n", base, top, top - base + 1);
+               regcache_rbtree_get_base_top_reg(map, n, &base, &top);
+               this_registers = ((top - base) / map->reg_stride) + 1;
+               seq_printf(s, "%x-%x (%d)\n", base, top, this_registers);
 
                nodes++;
-               registers += top - base + 1;
+               registers += this_registers;
        }
 
        if (nodes)
@@ -161,7 +165,7 @@ static int rbtree_show(struct seq_file *s, void *ignored)
        seq_printf(s, "%d nodes, %d registers, average %d registers\n",
                   nodes, registers, average);
 
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 
        return 0;
 }
@@ -255,7 +259,7 @@ static int regcache_rbtree_read(struct regmap *map,
 
        rbnode = regcache_rbtree_lookup(map, reg);
        if (rbnode) {
-               reg_tmp = reg - rbnode->base_reg;
+               reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
                *value = regcache_rbtree_get_register(rbnode, reg_tmp,
                                                      map->cache_word_size);
        } else {
@@ -310,7 +314,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
         */
        rbnode = regcache_rbtree_lookup(map, reg);
        if (rbnode) {
-               reg_tmp = reg - rbnode->base_reg;
+               reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
                val = regcache_rbtree_get_register(rbnode, reg_tmp,
                                                   map->cache_word_size);
                if (val == value)
@@ -321,13 +325,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
                /* look for an adjacent register to the one we are about to add */
                for (node = rb_first(&rbtree_ctx->root); node;
                     node = rb_next(node)) {
-                       rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node);
+                       rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
+                                             node);
                        for (i = 0; i < rbnode_tmp->blklen; i++) {
-                               reg_tmp = rbnode_tmp->base_reg + i;
-                               if (abs(reg_tmp - reg) != 1)
+                               reg_tmp = rbnode_tmp->base_reg +
+                                               (i * map->reg_stride);
+                               if (abs(reg_tmp - reg) != map->reg_stride)
                                        continue;
                                /* decide where in the block to place our register */
-                               if (reg_tmp + 1 == reg)
+                               if (reg_tmp + map->reg_stride == reg)
                                        pos = i + 1;
                                else
                                        pos = i;
@@ -357,7 +363,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
                        return -ENOMEM;
                }
                regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size);
-               regcache_rbtree_insert(&rbtree_ctx->root, rbnode);
+               regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode);
                rbtree_ctx->cached_rbnode = rbnode;
        }
 
@@ -397,7 +403,7 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
                        end = rbnode->blklen;
 
                for (i = base; i < end; i++) {
-                       regtmp = rbnode->base_reg + i;
+                       regtmp = rbnode->base_reg + (i * map->reg_stride);
                        val = regcache_rbtree_get_register(rbnode, i,
                                                           map->cache_word_size);
 
index 74b69095def6def7129d77a142936e7b751705d1..835883bda977c27cdeb1d5ec45e63a6e69c47269 100644 (file)
@@ -59,7 +59,7 @@ static int regcache_hw_init(struct regmap *map)
        for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) {
                val = regcache_get_val(map->reg_defaults_raw,
                                       i, map->cache_word_size);
-               if (regmap_volatile(map, i))
+               if (regmap_volatile(map, i * map->reg_stride))
                        continue;
                count++;
        }
@@ -76,9 +76,9 @@ static int regcache_hw_init(struct regmap *map)
        for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
                val = regcache_get_val(map->reg_defaults_raw,
                                       i, map->cache_word_size);
-               if (regmap_volatile(map, i))
+               if (regmap_volatile(map, i * map->reg_stride))
                        continue;
-               map->reg_defaults[j].reg = i;
+               map->reg_defaults[j].reg = i * map->reg_stride;
                map->reg_defaults[j].def = val;
                j++;
        }
@@ -98,6 +98,10 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
        int i;
        void *tmp_buf;
 
+       for (i = 0; i < config->num_reg_defaults; i++)
+               if (config->reg_defaults[i].reg % map->reg_stride)
+                       return -EINVAL;
+
        if (map->cache_type == REGCACHE_NONE) {
                map->cache_bypass = true;
                return 0;
@@ -264,7 +268,7 @@ int regcache_sync(struct regmap *map)
 
        BUG_ON(!map->cache_ops || !map->cache_ops->sync);
 
-       mutex_lock(&map->lock);
+       map->lock(map);
        /* Remember the initial bypass state */
        bypass = map->cache_bypass;
        dev_dbg(map->dev, "Syncing %s cache\n",
@@ -278,6 +282,10 @@ int regcache_sync(struct regmap *map)
        /* Apply any patch first */
        map->cache_bypass = 1;
        for (i = 0; i < map->patch_regs; i++) {
+               if (map->patch[i].reg % map->reg_stride) {
+                       ret = -EINVAL;
+                       goto out;
+               }
                ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def);
                if (ret != 0) {
                        dev_err(map->dev, "Failed to write %x = %x: %d\n",
@@ -296,7 +304,7 @@ out:
        trace_regcache_sync(map->dev, name, "stop");
        /* Restore the bypass state */
        map->cache_bypass = bypass;
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 
        return ret;
 }
@@ -323,7 +331,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
 
        BUG_ON(!map->cache_ops || !map->cache_ops->sync);
 
-       mutex_lock(&map->lock);
+       map->lock(map);
 
        /* Remember the initial bypass state */
        bypass = map->cache_bypass;
@@ -342,7 +350,7 @@ out:
        trace_regcache_sync(map->dev, name, "stop region");
        /* Restore the bypass state */
        map->cache_bypass = bypass;
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 
        return ret;
 }
@@ -362,11 +370,11 @@ EXPORT_SYMBOL_GPL(regcache_sync_region);
  */
 void regcache_cache_only(struct regmap *map, bool enable)
 {
-       mutex_lock(&map->lock);
+       map->lock(map);
        WARN_ON(map->cache_bypass && enable);
        map->cache_only = enable;
        trace_regmap_cache_only(map->dev, enable);
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 }
 EXPORT_SYMBOL_GPL(regcache_cache_only);
 
@@ -381,9 +389,9 @@ EXPORT_SYMBOL_GPL(regcache_cache_only);
  */
 void regcache_mark_dirty(struct regmap *map)
 {
-       mutex_lock(&map->lock);
+       map->lock(map);
        map->cache_dirty = true;
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 }
 EXPORT_SYMBOL_GPL(regcache_mark_dirty);
 
@@ -400,11 +408,11 @@ EXPORT_SYMBOL_GPL(regcache_mark_dirty);
  */
 void regcache_cache_bypass(struct regmap *map, bool enable)
 {
-       mutex_lock(&map->lock);
+       map->lock(map);
        WARN_ON(map->cache_only && enable);
        map->cache_bypass = enable;
        trace_regmap_cache_bypass(map->dev, enable);
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 }
 EXPORT_SYMBOL_GPL(regcache_cache_bypass);
 
index 251eb70f83e7c0f626f6fd73ba86ec40e76d339f..bb1ff175b9629083a4d5d6e4e0e6811de3b2f808 100644 (file)
@@ -80,7 +80,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
        val_len = 2 * map->format.val_bytes;
        tot_len = reg_len + val_len + 3;      /* : \n */
 
-       for (i = 0; i < map->max_register + 1; i++) {
+       for (i = 0; i <= map->max_register; i += map->reg_stride) {
                if (!regmap_readable(map, i))
                        continue;
 
@@ -197,7 +197,7 @@ static ssize_t regmap_access_read_file(struct file *file,
        reg_len = regmap_calc_reg_len(map->max_register, buf, count);
        tot_len = reg_len + 10; /* ': R W V P\n' */
 
-       for (i = 0; i < map->max_register + 1; i++) {
+       for (i = 0; i <= map->max_register; i += map->reg_stride) {
                /* Ignore registers which are neither readable nor writable */
                if (!regmap_readable(map, i) && !regmap_writeable(map, i))
                        continue;
@@ -242,10 +242,17 @@ static const struct file_operations regmap_access_fops = {
        .llseek = default_llseek,
 };
 
-void regmap_debugfs_init(struct regmap *map)
+void regmap_debugfs_init(struct regmap *map, const char *name)
 {
-       map->debugfs = debugfs_create_dir(dev_name(map->dev),
-                                         regmap_debugfs_root);
+       if (name) {
+               map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
+                                             dev_name(map->dev), name);
+               name = map->debugfs_name;
+       } else {
+               name = dev_name(map->dev);
+       }
+
+       map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
        if (!map->debugfs) {
                dev_warn(map->dev, "Failed to create debugfs directory\n");
                return;
@@ -274,6 +281,7 @@ void regmap_debugfs_init(struct regmap *map)
 void regmap_debugfs_exit(struct regmap *map)
 {
        debugfs_remove_recursive(map->debugfs);
+       kfree(map->debugfs_name);
 }
 
 void regmap_debugfs_initcall(void)
index 9a3a8c5643892b3e81b2f0c99871f20a1ed9baec..5f6b2478bf1759717e9c85f1958207364f3f7520 100644 (file)
@@ -15,8 +15,9 @@
 #include <linux/module.h>
 #include <linux/init.h>
 
-static int regmap_i2c_write(struct device *dev, const void *data, size_t count)
+static int regmap_i2c_write(void *context, const void *data, size_t count)
 {
+       struct device *dev = context;
        struct i2c_client *i2c = to_i2c_client(dev);
        int ret;
 
@@ -29,10 +30,11 @@ static int regmap_i2c_write(struct device *dev, const void *data, size_t count)
                return -EIO;
 }
 
-static int regmap_i2c_gather_write(struct device *dev,
+static int regmap_i2c_gather_write(void *context,
                                   const void *reg, size_t reg_size,
                                   const void *val, size_t val_size)
 {
+       struct device *dev = context;
        struct i2c_client *i2c = to_i2c_client(dev);
        struct i2c_msg xfer[2];
        int ret;
@@ -62,10 +64,11 @@ static int regmap_i2c_gather_write(struct device *dev,
                return -EIO;
 }
 
-static int regmap_i2c_read(struct device *dev,
+static int regmap_i2c_read(void *context,
                           const void *reg, size_t reg_size,
                           void *val, size_t val_size)
 {
+       struct device *dev = context;
        struct i2c_client *i2c = to_i2c_client(dev);
        struct i2c_msg xfer[2];
        int ret;
@@ -107,7 +110,7 @@ static struct regmap_bus regmap_i2c = {
 struct regmap *regmap_init_i2c(struct i2c_client *i2c,
                               const struct regmap_config *config)
 {
-       return regmap_init(&i2c->dev, &regmap_i2c, config);
+       return regmap_init(&i2c->dev, &regmap_i2c, &i2c->dev, config);
 }
 EXPORT_SYMBOL_GPL(regmap_init_i2c);
 
@@ -124,7 +127,7 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c);
 struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
                                    const struct regmap_config *config)
 {
-       return devm_regmap_init(&i2c->dev, &regmap_i2c, config);
+       return devm_regmap_init(&i2c->dev, &regmap_i2c, &i2c->dev, config);
 }
 EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
 
index 1befaa7a31cbdeacb76a7a67f17540799f523d6b..fc69d29d272aa56cea9d8cd9910e2c4399f26c12 100644 (file)
@@ -50,6 +50,7 @@ static void regmap_irq_lock(struct irq_data *data)
 static void regmap_irq_sync_unlock(struct irq_data *data)
 {
        struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
+       struct regmap *map = d->map;
        int i, ret;
 
        /*
@@ -58,11 +59,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
         * suppress pointless writes.
         */
        for (i = 0; i < d->chip->num_regs; i++) {
-               ret = regmap_update_bits(d->map, d->chip->mask_base + i,
+               ret = regmap_update_bits(d->map, d->chip->mask_base +
+                                               (i * map->reg_stride),
                                         d->mask_buf_def[i], d->mask_buf[i]);
                if (ret != 0)
                        dev_err(d->map->dev, "Failed to sync masks in %x\n",
-                               d->chip->mask_base + i);
+                               d->chip->mask_base + (i * map->reg_stride));
        }
 
        mutex_unlock(&d->lock);
@@ -71,17 +73,19 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
 static void regmap_irq_enable(struct irq_data *data)
 {
        struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
+       struct regmap *map = d->map;
        const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq);
 
-       d->mask_buf[irq_data->reg_offset] &= ~irq_data->mask;
+       d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask;
 }
 
 static void regmap_irq_disable(struct irq_data *data)
 {
        struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
+       struct regmap *map = d->map;
        const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq);
 
-       d->mask_buf[irq_data->reg_offset] |= irq_data->mask;
+       d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
 }
 
 static struct irq_chip regmap_irq_chip = {
@@ -136,17 +140,19 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
                data->status_buf[i] &= ~data->mask_buf[i];
 
                if (data->status_buf[i] && chip->ack_base) {
-                       ret = regmap_write(map, chip->ack_base + i,
+                       ret = regmap_write(map, chip->ack_base +
+                                               (i * map->reg_stride),
                                           data->status_buf[i]);
                        if (ret != 0)
                                dev_err(map->dev, "Failed to ack 0x%x: %d\n",
-                                       chip->ack_base + i, ret);
+                                       chip->ack_base + (i * map->reg_stride),
+                                       ret);
                }
        }
 
        for (i = 0; i < chip->num_irqs; i++) {
-               if (data->status_buf[chip->irqs[i].reg_offset] &
-                   chip->irqs[i].mask) {
+               if (data->status_buf[chip->irqs[i].reg_offset /
+                                    map->reg_stride] & chip->irqs[i].mask) {
                        handle_nested_irq(data->irq_base + i);
                        handled = true;
                }
@@ -181,6 +187,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
        int cur_irq, i;
        int ret = -ENOMEM;
 
+       for (i = 0; i < chip->num_irqs; i++) {
+               if (chip->irqs[i].reg_offset % map->reg_stride)
+                       return -EINVAL;
+               if (chip->irqs[i].reg_offset / map->reg_stride >=
+                   chip->num_regs)
+                       return -EINVAL;
+       }
+
        irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
        if (irq_base < 0) {
                dev_warn(map->dev, "Failed to allocate IRQs: %d\n",
@@ -218,16 +232,17 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
        mutex_init(&d->lock);
 
        for (i = 0; i < chip->num_irqs; i++)
-               d->mask_buf_def[chip->irqs[i].reg_offset]
+               d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride]
                        |= chip->irqs[i].mask;
 
        /* Mask all the interrupts by default */
        for (i = 0; i < chip->num_regs; i++) {
                d->mask_buf[i] = d->mask_buf_def[i];
-               ret = regmap_write(map, chip->mask_base + i, d->mask_buf[i]);
+               ret = regmap_write(map, chip->mask_base + (i * map->reg_stride),
+                                  d->mask_buf[i]);
                if (ret != 0) {
                        dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
-                               chip->mask_base + i, ret);
+                               chip->mask_base + (i * map->reg_stride), ret);
                        goto err_alloc;
                }
        }
diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c
new file mode 100644 (file)
index 0000000..febd6de
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Register map access API - MMIO support
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+struct regmap_mmio_context {
+       void __iomem *regs;
+       unsigned val_bytes;
+};
+
+static int regmap_mmio_gather_write(void *context,
+                                   const void *reg, size_t reg_size,
+                                   const void *val, size_t val_size)
+{
+       struct regmap_mmio_context *ctx = context;
+       u32 offset;
+
+       BUG_ON(reg_size != 4);
+
+       offset = be32_to_cpup(reg);
+
+       while (val_size) {
+               switch (ctx->val_bytes) {
+               case 1:
+                       writeb(*(u8 *)val, ctx->regs + offset);
+                       break;
+               case 2:
+                       writew(be16_to_cpup(val), ctx->regs + offset);
+                       break;
+               case 4:
+                       writel(be32_to_cpup(val), ctx->regs + offset);
+                       break;
+#ifdef CONFIG_64BIT
+               case 8:
+                       writeq(be64_to_cpup(val), ctx->regs + offset);
+                       break;
+#endif
+               default:
+                       /* Should be caught by regmap_mmio_check_config */
+                       BUG();
+               }
+               val_size -= ctx->val_bytes;
+               val += ctx->val_bytes;
+               offset += ctx->val_bytes;
+       }
+
+       return 0;
+}
+
+static int regmap_mmio_write(void *context, const void *data, size_t count)
+{
+       BUG_ON(count < 4);
+
+       return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4);
+}
+
+static int regmap_mmio_read(void *context,
+                           const void *reg, size_t reg_size,
+                           void *val, size_t val_size)
+{
+       struct regmap_mmio_context *ctx = context;
+       u32 offset;
+
+       BUG_ON(reg_size != 4);
+
+       offset = be32_to_cpup(reg);
+
+       while (val_size) {
+               switch (ctx->val_bytes) {
+               case 1:
+                       *(u8 *)val = readb(ctx->regs + offset);
+                       break;
+               case 2:
+                       *(u16 *)val = cpu_to_be16(readw(ctx->regs + offset));
+                       break;
+               case 4:
+                       *(u32 *)val = cpu_to_be32(readl(ctx->regs + offset));
+                       break;
+#ifdef CONFIG_64BIT
+               case 8:
+                       *(u64 *)val = cpu_to_be32(readq(ctx->regs + offset));
+                       break;
+#endif
+               default:
+                       /* Should be caught by regmap_mmio_check_config */
+                       BUG();
+               }
+               val_size -= ctx->val_bytes;
+               val += ctx->val_bytes;
+               offset += ctx->val_bytes;
+       }
+
+       return 0;
+}
+
+static void regmap_mmio_free_context(void *context)
+{
+       kfree(context);
+}
+
+static struct regmap_bus regmap_mmio = {
+       .fast_io = true,
+       .write = regmap_mmio_write,
+       .gather_write = regmap_mmio_gather_write,
+       .read = regmap_mmio_read,
+       .free_context = regmap_mmio_free_context,
+};
+
+struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
+                                       const struct regmap_config *config)
+{
+       struct regmap_mmio_context *ctx;
+       int min_stride;
+
+       if (config->reg_bits != 32)
+               return ERR_PTR(-EINVAL);
+
+       if (config->pad_bits)
+               return ERR_PTR(-EINVAL);
+
+       switch (config->val_bits) {
+       case 8:
+               /* The core treats 0 as 1 */
+               min_stride = 0;
+               break;
+       case 16:
+               min_stride = 2;
+               break;
+       case 32:
+               min_stride = 4;
+               break;
+#ifdef CONFIG_64BIT
+       case 64:
+               min_stride = 8;
+               break;
+#endif
+               break;
+       default:
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (config->reg_stride < min_stride)
+               return ERR_PTR(-EINVAL);
+
+       ctx = kzalloc(GFP_KERNEL, sizeof(*ctx));
+       if (!ctx)
+               return ERR_PTR(-ENOMEM);
+
+       ctx->regs = regs;
+       ctx->val_bytes = config->val_bits / 8;
+
+       return ctx;
+}
+
+/**
+ * regmap_init_mmio(): Initialise register map
+ *
+ * @dev: Device that will be interacted with
+ * @regs: Pointer to memory-mapped IO region
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+struct regmap *regmap_init_mmio(struct device *dev,
+                               void __iomem *regs,
+                               const struct regmap_config *config)
+{
+       struct regmap_mmio_context *ctx;
+
+       ctx = regmap_mmio_gen_context(regs, config);
+       if (IS_ERR(ctx))
+               return ERR_CAST(ctx);
+
+       return regmap_init(dev, &regmap_mmio, ctx, config);
+}
+EXPORT_SYMBOL_GPL(regmap_init_mmio);
+
+/**
+ * devm_regmap_init_mmio(): Initialise managed register map
+ *
+ * @dev: Device that will be interacted with
+ * @regs: Pointer to memory-mapped IO region
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap.  The regmap will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_mmio(struct device *dev,
+                                    void __iomem *regs,
+                                    const struct regmap_config *config)
+{
+       struct regmap_mmio_context *ctx;
+
+       ctx = regmap_mmio_gen_context(regs, config);
+       if (IS_ERR(ctx))
+               return ERR_CAST(ctx);
+
+       return devm_regmap_init(dev, &regmap_mmio, ctx, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_mmio);
+
+MODULE_LICENSE("GPL v2");
index 7c0c35a39c331afa9e5c5d940e8bf16354aa4a10..ffa46a92ad3383dcc8609cd891514f0b5c3af967 100644 (file)
 #include <linux/init.h>
 #include <linux/module.h>
 
-static int regmap_spi_write(struct device *dev, const void *data, size_t count)
+static int regmap_spi_write(void *context, const void *data, size_t count)
 {
+       struct device *dev = context;
        struct spi_device *spi = to_spi_device(dev);
 
        return spi_write(spi, data, count);
 }
 
-static int regmap_spi_gather_write(struct device *dev,
+static int regmap_spi_gather_write(void *context,
                                   const void *reg, size_t reg_len,
                                   const void *val, size_t val_len)
 {
+       struct device *dev = context;
        struct spi_device *spi = to_spi_device(dev);
        struct spi_message m;
        struct spi_transfer t[2] = { { .tx_buf = reg, .len = reg_len, },
@@ -38,10 +40,11 @@ static int regmap_spi_gather_write(struct device *dev,
        return spi_sync(spi, &m);
 }
 
-static int regmap_spi_read(struct device *dev,
+static int regmap_spi_read(void *context,
                           const void *reg, size_t reg_size,
                           void *val, size_t val_size)
 {
+       struct device *dev = context;
        struct spi_device *spi = to_spi_device(dev);
 
        return spi_write_then_read(spi, reg, reg_size, val, val_size);
@@ -66,7 +69,7 @@ static struct regmap_bus regmap_spi = {
 struct regmap *regmap_init_spi(struct spi_device *spi,
                               const struct regmap_config *config)
 {
-       return regmap_init(&spi->dev, &regmap_spi, config);
+       return regmap_init(&spi->dev, &regmap_spi, &spi->dev, config);
 }
 EXPORT_SYMBOL_GPL(regmap_init_spi);
 
@@ -83,7 +86,7 @@ EXPORT_SYMBOL_GPL(regmap_init_spi);
 struct regmap *devm_regmap_init_spi(struct spi_device *spi,
                                    const struct regmap_config *config)
 {
-       return devm_regmap_init(&spi->dev, &regmap_spi, config);
+       return devm_regmap_init(&spi->dev, &regmap_spi, &spi->dev, config);
 }
 EXPORT_SYMBOL_GPL(devm_regmap_init_spi);
 
index bb80853ff27a9d25e1003a25edec0d21cbe8c2ad..357294905793c840aea6826262bbab4dda2d3660 100644 (file)
@@ -112,25 +112,36 @@ static void regmap_format_10_14_write(struct regmap *map,
        out[0] = reg >> 2;
 }
 
-static void regmap_format_8(void *buf, unsigned int val)
+static void regmap_format_8(void *buf, unsigned int val, unsigned int shift)
 {
        u8 *b = buf;
 
-       b[0] = val;
+       b[0] = val << shift;
 }
 
-static void regmap_format_16(void *buf, unsigned int val)
+static void regmap_format_16(void *buf, unsigned int val, unsigned int shift)
 {
        __be16 *b = buf;
 
-       b[0] = cpu_to_be16(val);
+       b[0] = cpu_to_be16(val << shift);
 }
 
-static void regmap_format_32(void *buf, unsigned int val)
+static void regmap_format_24(void *buf, unsigned int val, unsigned int shift)
+{
+       u8 *b = buf;
+
+       val <<= shift;
+
+       b[0] = val >> 16;
+       b[1] = val >> 8;
+       b[2] = val;
+}
+
+static void regmap_format_32(void *buf, unsigned int val, unsigned int shift)
 {
        __be32 *b = buf;
 
-       b[0] = cpu_to_be32(val);
+       b[0] = cpu_to_be32(val << shift);
 }
 
 static unsigned int regmap_parse_8(void *buf)
@@ -149,6 +160,16 @@ static unsigned int regmap_parse_16(void *buf)
        return b[0];
 }
 
+static unsigned int regmap_parse_24(void *buf)
+{
+       u8 *b = buf;
+       unsigned int ret = b[2];
+       ret |= ((unsigned int)b[1]) << 8;
+       ret |= ((unsigned int)b[0]) << 16;
+
+       return ret;
+}
+
 static unsigned int regmap_parse_32(void *buf)
 {
        __be32 *b = buf;
@@ -158,11 +179,32 @@ static unsigned int regmap_parse_32(void *buf)
        return b[0];
 }
 
+static void regmap_lock_mutex(struct regmap *map)
+{
+       mutex_lock(&map->mutex);
+}
+
+static void regmap_unlock_mutex(struct regmap *map)
+{
+       mutex_unlock(&map->mutex);
+}
+
+static void regmap_lock_spinlock(struct regmap *map)
+{
+       spin_lock(&map->spinlock);
+}
+
+static void regmap_unlock_spinlock(struct regmap *map)
+{
+       spin_unlock(&map->spinlock);
+}
+
 /**
  * regmap_init(): Initialise register map
  *
  * @dev: Device that will be interacted with
  * @bus: Bus-specific callbacks to use with device
+ * @bus_context: Data passed to bus-specific callbacks
  * @config: Configuration for register map
  *
  * The return value will be an ERR_PTR() on error or a valid pointer to
@@ -171,6 +213,7 @@ static unsigned int regmap_parse_32(void *buf)
  */
 struct regmap *regmap_init(struct device *dev,
                           const struct regmap_bus *bus,
+                          void *bus_context,
                           const struct regmap_config *config)
 {
        struct regmap *map;
@@ -185,14 +228,29 @@ struct regmap *regmap_init(struct device *dev,
                goto err;
        }
 
-       mutex_init(&map->lock);
+       if (bus->fast_io) {
+               spin_lock_init(&map->spinlock);
+               map->lock = regmap_lock_spinlock;
+               map->unlock = regmap_unlock_spinlock;
+       } else {
+               mutex_init(&map->mutex);
+               map->lock = regmap_lock_mutex;
+               map->unlock = regmap_unlock_mutex;
+       }
        map->format.buf_size = (config->reg_bits + config->val_bits) / 8;
        map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
        map->format.pad_bytes = config->pad_bits / 8;
        map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
        map->format.buf_size += map->format.pad_bytes;
+       map->reg_shift = config->pad_bits % 8;
+       if (config->reg_stride)
+               map->reg_stride = config->reg_stride;
+       else
+               map->reg_stride = 1;
+       map->use_single_rw = config->use_single_rw;
        map->dev = dev;
        map->bus = bus;
+       map->bus_context = bus_context;
        map->max_register = config->max_register;
        map->writeable_reg = config->writeable_reg;
        map->readable_reg = config->readable_reg;
@@ -207,7 +265,7 @@ struct regmap *regmap_init(struct device *dev,
                map->read_flag_mask = bus->read_flag_mask;
        }
 
-       switch (config->reg_bits) {
+       switch (config->reg_bits + map->reg_shift) {
        case 2:
                switch (config->val_bits) {
                case 6:
@@ -273,12 +331,19 @@ struct regmap *regmap_init(struct device *dev,
                map->format.format_val = regmap_format_16;
                map->format.parse_val = regmap_parse_16;
                break;
+       case 24:
+               map->format.format_val = regmap_format_24;
+               map->format.parse_val = regmap_parse_24;
+               break;
        case 32:
                map->format.format_val = regmap_format_32;
                map->format.parse_val = regmap_parse_32;
                break;
        }
 
+       if (map->format.format_write)
+               map->use_single_rw = true;
+
        if (!map->format.format_write &&
            !(map->format.format_reg && map->format.format_val))
                goto err_map;
@@ -289,7 +354,7 @@ struct regmap *regmap_init(struct device *dev,
                goto err_map;
        }
 
-       regmap_debugfs_init(map);
+       regmap_debugfs_init(map, config->name);
 
        ret = regcache_init(map, config);
        if (ret < 0)
@@ -316,6 +381,7 @@ static void devm_regmap_release(struct device *dev, void *res)
  *
  * @dev: Device that will be interacted with
  * @bus: Bus-specific callbacks to use with device
+ * @bus_context: Data passed to bus-specific callbacks
  * @config: Configuration for register map
  *
  * The return value will be an ERR_PTR() on error or a valid pointer
@@ -325,6 +391,7 @@ static void devm_regmap_release(struct device *dev, void *res)
  */
 struct regmap *devm_regmap_init(struct device *dev,
                                const struct regmap_bus *bus,
+                               void *bus_context,
                                const struct regmap_config *config)
 {
        struct regmap **ptr, *regmap;
@@ -333,7 +400,7 @@ struct regmap *devm_regmap_init(struct device *dev,
        if (!ptr)
                return ERR_PTR(-ENOMEM);
 
-       regmap = regmap_init(dev, bus, config);
+       regmap = regmap_init(dev, bus, bus_context, config);
        if (!IS_ERR(regmap)) {
                *ptr = regmap;
                devres_add(dev, ptr);
@@ -360,7 +427,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
 {
        int ret;
 
-       mutex_lock(&map->lock);
+       map->lock(map);
 
        regcache_exit(map);
        regmap_debugfs_exit(map);
@@ -372,14 +439,14 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
        map->precious_reg = config->precious_reg;
        map->cache_type = config->cache_type;
 
-       regmap_debugfs_init(map);
+       regmap_debugfs_init(map, config->name);
 
        map->cache_bypass = false;
        map->cache_only = false;
 
        ret = regcache_init(map, config);
 
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 
        return ret;
 }
@@ -391,6 +458,8 @@ void regmap_exit(struct regmap *map)
 {
        regcache_exit(map);
        regmap_debugfs_exit(map);
+       if (map->bus->free_context)
+               map->bus->free_context(map->bus_context);
        kfree(map->work_buf);
        kfree(map);
 }
@@ -408,7 +477,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
        /* Check for unwritable registers before we start */
        if (map->writeable_reg)
                for (i = 0; i < val_len / map->format.val_bytes; i++)
-                       if (!map->writeable_reg(map->dev, reg + i))
+                       if (!map->writeable_reg(map->dev,
+                                               reg + (i * map->reg_stride)))
                                return -EINVAL;
 
        if (!map->cache_bypass && map->format.parse_val) {
@@ -417,7 +487,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
                for (i = 0; i < val_len / val_bytes; i++) {
                        memcpy(map->work_buf, val + (i * val_bytes), val_bytes);
                        ival = map->format.parse_val(map->work_buf);
-                       ret = regcache_write(map, reg + i, ival);
+                       ret = regcache_write(map, reg + (i * map->reg_stride),
+                                            ival);
                        if (ret) {
                                dev_err(map->dev,
                                   "Error in caching of register: %u ret: %d\n",
@@ -431,7 +502,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
                }
        }
 
-       map->format.format_reg(map->work_buf, reg);
+       map->format.format_reg(map->work_buf, reg, map->reg_shift);
 
        u8[0] |= map->write_flag_mask;
 
@@ -444,12 +515,12 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
         */
        if (val == (map->work_buf + map->format.pad_bytes +
                    map->format.reg_bytes))
-               ret = map->bus->write(map->dev, map->work_buf,
+               ret = map->bus->write(map->bus_context, map->work_buf,
                                      map->format.reg_bytes +
                                      map->format.pad_bytes +
                                      val_len);
        else if (map->bus->gather_write)
-               ret = map->bus->gather_write(map->dev, map->work_buf,
+               ret = map->bus->gather_write(map->bus_context, map->work_buf,
                                             map->format.reg_bytes +
                                             map->format.pad_bytes,
                                             val, val_len);
@@ -464,7 +535,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
                memcpy(buf, map->work_buf, map->format.reg_bytes);
                memcpy(buf + map->format.reg_bytes + map->format.pad_bytes,
                       val, val_len);
-               ret = map->bus->write(map->dev, buf, len);
+               ret = map->bus->write(map->bus_context, buf, len);
 
                kfree(buf);
        }
@@ -498,7 +569,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
 
                trace_regmap_hw_write_start(map->dev, reg, 1);
 
-               ret = map->bus->write(map->dev, map->work_buf,
+               ret = map->bus->write(map->bus_context, map->work_buf,
                                      map->format.buf_size);
 
                trace_regmap_hw_write_done(map->dev, reg, 1);
@@ -506,7 +577,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
                return ret;
        } else {
                map->format.format_val(map->work_buf + map->format.reg_bytes
-                                      + map->format.pad_bytes, val);
+                                      + map->format.pad_bytes, val, 0);
                return _regmap_raw_write(map, reg,
                                         map->work_buf +
                                         map->format.reg_bytes +
@@ -529,11 +600,14 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
 {
        int ret;
 
-       mutex_lock(&map->lock);
+       if (reg % map->reg_stride)
+               return -EINVAL;
+
+       map->lock(map);
 
        ret = _regmap_write(map, reg, val);
 
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 
        return ret;
 }
@@ -560,11 +634,16 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
 {
        int ret;
 
-       mutex_lock(&map->lock);
+       if (val_len % map->format.val_bytes)
+               return -EINVAL;
+       if (reg % map->reg_stride)
+               return -EINVAL;
+
+       map->lock(map);
 
        ret = _regmap_raw_write(map, reg, val, val_len);
 
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 
        return ret;
 }
@@ -593,8 +672,10 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 
        if (!map->format.parse_val)
                return -EINVAL;
+       if (reg % map->reg_stride)
+               return -EINVAL;
 
-       mutex_lock(&map->lock);
+       map->lock(map);
 
        /* No formatting is require if val_byte is 1 */
        if (val_bytes == 1) {
@@ -609,13 +690,28 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
                for (i = 0; i < val_count * val_bytes; i += val_bytes)
                        map->format.parse_val(wval + i);
        }
-       ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
+       /*
+        * Some devices does not support bulk write, for
+        * them we have a series of single write operations.
+        */
+       if (map->use_single_rw) {
+               for (i = 0; i < val_count; i++) {
+                       ret = regmap_raw_write(map,
+                                               reg + (i * map->reg_stride),
+                                               val + (i * val_bytes),
+                                               val_bytes);
+                       if (ret != 0)
+                               return ret;
+               }
+       } else {
+               ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
+       }
 
        if (val_bytes != 1)
                kfree(wval);
 
 out:
-       mutex_unlock(&map->lock);
+       map->unlock(map);
        return ret;
 }
 EXPORT_SYMBOL_GPL(regmap_bulk_write);
@@ -626,7 +722,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
        u8 *u8 = map->work_buf;
        int ret;
 
-       map->format.format_reg(map->work_buf, reg);
+       map->format.format_reg(map->work_buf, reg, map->reg_shift);
 
        /*
         * Some buses or devices flag reads by setting the high bits in the
@@ -639,7 +735,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
        trace_regmap_hw_read_start(map->dev, reg,
                                   val_len / map->format.val_bytes);
 
-       ret = map->bus->read(map->dev, map->work_buf,
+       ret = map->bus->read(map->bus_context, map->work_buf,
                             map->format.reg_bytes + map->format.pad_bytes,
                             val, val_len);
 
@@ -689,11 +785,14 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
 {
        int ret;
 
-       mutex_lock(&map->lock);
+       if (reg % map->reg_stride)
+               return -EINVAL;
+
+       map->lock(map);
 
        ret = _regmap_read(map, reg, val);
 
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 
        return ret;
 }
@@ -718,7 +817,12 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
        unsigned int v;
        int ret, i;
 
-       mutex_lock(&map->lock);
+       if (val_len % map->format.val_bytes)
+               return -EINVAL;
+       if (reg % map->reg_stride)
+               return -EINVAL;
+
+       map->lock(map);
 
        if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass ||
            map->cache_type == REGCACHE_NONE) {
@@ -730,16 +834,17 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
                 * cost as we expect to hit the cache.
                 */
                for (i = 0; i < val_count; i++) {
-                       ret = _regmap_read(map, reg + i, &v);
+                       ret = _regmap_read(map, reg + (i * map->reg_stride),
+                                          &v);
                        if (ret != 0)
                                goto out;
 
-                       map->format.format_val(val + (i * val_bytes), v);
+                       map->format.format_val(val + (i * val_bytes), v, 0);
                }
        }
 
  out:
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 
        return ret;
 }
@@ -765,18 +870,37 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
 
        if (!map->format.parse_val)
                return -EINVAL;
+       if (reg % map->reg_stride)
+               return -EINVAL;
 
        if (vol || map->cache_type == REGCACHE_NONE) {
-               ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
-               if (ret != 0)
-                       return ret;
+               /*
+                * Some devices does not support bulk read, for
+                * them we have a series of single read operations.
+                */
+               if (map->use_single_rw) {
+                       for (i = 0; i < val_count; i++) {
+                               ret = regmap_raw_read(map,
+                                               reg + (i * map->reg_stride),
+                                               val + (i * val_bytes),
+                                               val_bytes);
+                               if (ret != 0)
+                                       return ret;
+                       }
+               } else {
+                       ret = regmap_raw_read(map, reg, val,
+                                             val_bytes * val_count);
+                       if (ret != 0)
+                               return ret;
+               }
 
                for (i = 0; i < val_count * val_bytes; i += val_bytes)
                        map->format.parse_val(val + i);
        } else {
                for (i = 0; i < val_count; i++) {
                        unsigned int ival;
-                       ret = regmap_read(map, reg + i, &ival);
+                       ret = regmap_read(map, reg + (i * map->reg_stride),
+                                         &ival);
                        if (ret != 0)
                                return ret;
                        memcpy(val + (i * val_bytes), &ival, val_bytes);
@@ -794,7 +918,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
        int ret;
        unsigned int tmp, orig;
 
-       mutex_lock(&map->lock);
+       map->lock(map);
 
        ret = _regmap_read(map, reg, &orig);
        if (ret != 0)
@@ -811,7 +935,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
        }
 
 out:
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 
        return ret;
 }
@@ -878,7 +1002,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
        if (map->patch)
                return -EBUSY;
 
-       mutex_lock(&map->lock);
+       map->lock(map);
 
        bypass = map->cache_bypass;
 
@@ -906,7 +1030,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
 out:
        map->cache_bypass = bypass;
 
-       mutex_unlock(&map->lock);
+       map->unlock(map);
 
        return ret;
 }
index a90abb6bfa6400402b516865df47db182dd5e0a0..ae797b142aa8cb5f5eeb2fda088f6a5ee30426da 100644 (file)
@@ -46,7 +46,13 @@ struct reg_default {
 /**
  * Configuration for the register map of a device.
  *
+ * @name: Optional name of the regmap. Useful when a device has multiple
+ *        register regions.
+ *
  * @reg_bits: Number of bits in a register address, mandatory.
+ * @reg_stride: The register address stride. Valid register addresses are a
+ *              multiple of this value. If set to 0, a value of 1 will be
+ *              used.
  * @pad_bits: Number of bits of padding between register and value.
  * @val_bits: Number of bits in a register value, mandatory.
  *
@@ -70,6 +76,9 @@ struct reg_default {
  * @write_flag_mask: Mask to be set in the top byte of the register when doing
  *                   a write. If both read_flag_mask and write_flag_mask are
  *                   empty the regmap_bus default masks are used.
+ * @use_single_rw: If set, converts the bulk read and write operations into
+ *                 a series of single read and write operations. This is useful
+ *                 for device that does not support bulk read and write.
  *
  * @cache_type: The actual cache type.
  * @reg_defaults_raw: Power on reset values for registers (for use with
@@ -77,7 +86,10 @@ struct reg_default {
  * @num_reg_defaults_raw: Number of elements in reg_defaults_raw.
  */
 struct regmap_config {
+       const char *name;
+
        int reg_bits;
+       int reg_stride;
        int pad_bits;
        int val_bits;
 
@@ -95,20 +107,25 @@ struct regmap_config {
 
        u8 read_flag_mask;
        u8 write_flag_mask;
+
+       bool use_single_rw;
 };
 
-typedef int (*regmap_hw_write)(struct device *dev, const void *data,
+typedef int (*regmap_hw_write)(void *context, const void *data,
                               size_t count);
-typedef int (*regmap_hw_gather_write)(struct device *dev,
+typedef int (*regmap_hw_gather_write)(void *context,
                                      const void *reg, size_t reg_len,
                                      const void *val, size_t val_len);
-typedef int (*regmap_hw_read)(struct device *dev,
+typedef int (*regmap_hw_read)(void *context,
                              const void *reg_buf, size_t reg_size,
                              void *val_buf, size_t val_size);
+typedef void (*regmap_hw_free_context)(void *context);
 
 /**
  * Description of a hardware bus for the register map infrastructure.
  *
+ * @fast_io: Register IO is fast. Use a spinlock instead of a mutex
+ *           to perform locking.
  * @write: Write operation.
  * @gather_write: Write operation with split register/value, return -ENOTSUPP
  *                if not implemented  on a given device.
@@ -118,27 +135,37 @@ typedef int (*regmap_hw_read)(struct device *dev,
  *                  a read.
  */
 struct regmap_bus {
+       bool fast_io;
        regmap_hw_write write;
        regmap_hw_gather_write gather_write;
        regmap_hw_read read;
+       regmap_hw_free_context free_context;
        u8 read_flag_mask;
 };
 
 struct regmap *regmap_init(struct device *dev,
                           const struct regmap_bus *bus,
+                          void *bus_context,
                           const struct regmap_config *config);
 struct regmap *regmap_init_i2c(struct i2c_client *i2c,
                               const struct regmap_config *config);
 struct regmap *regmap_init_spi(struct spi_device *dev,
                               const struct regmap_config *config);
+struct regmap *regmap_init_mmio(struct device *dev,
+                               void __iomem *regs,
+                               const struct regmap_config *config);
 
 struct regmap *devm_regmap_init(struct device *dev,
                                const struct regmap_bus *bus,
+                               void *bus_context,
                                const struct regmap_config *config);
 struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
                                    const struct regmap_config *config);
 struct regmap *devm_regmap_init_spi(struct spi_device *dev,
                                    const struct regmap_config *config);
+struct regmap *devm_regmap_init_mmio(struct device *dev,
+                                    void __iomem *regs,
+                                    const struct regmap_config *config);
 
 void regmap_exit(struct regmap *map);
 int regmap_reinit_cache(struct regmap *map,