Merge branch 'for-linus' of git://opensource.wolfsonmicro.com/regmap
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 25 Oct 2011 11:57:45 +0000 (13:57 +0200)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 25 Oct 2011 11:57:45 +0000 (13:57 +0200)
* 'for-linus' of git://opensource.wolfsonmicro.com/regmap: (62 commits)
  mfd: Enable rbtree cache for wm831x devices
  regmap: Support some block operations on cached devices
  regmap: Allow caches for devices with no defaults
  regmap: Ensure rbtree syncs registers set to zero properly
  regmap: Allow rbtree to cache zero default values
  regmap: Warn on raw I/O as well as bulk reads that bypass cache
  regmap: Return a sensible error code if we fail to read the cache
  regmap: Use bsearch() to search the register defaults
  regmap: Fix doc comment
  regmap: Optimize the lookup path to use binary search
  regmap: Ensure we scream if we enable cache bypass/only at the same time
  regmap: Implement regcache_cache_bypass helper function
  regmap: Save/restore the bypass state upon syncing
  regmap: Lock the sync path, ensure we use the lockless _regmap_write()
  regmap: Fix apostrophe usage
  regmap: Make _regmap_write() global
  regmap: Fix lock used for regcache_cache_only()
  regmap: Grab the lock in regcache_cache_only()
  regmap: Modify map->cache_bypass directly
  regmap: Fix regcache_sync generic implementation
  ...

23 files changed:
drivers/base/regmap/Kconfig
drivers/base/regmap/Makefile
drivers/base/regmap/internal.h [new file with mode: 0644]
drivers/base/regmap/regcache-indexed.c [new file with mode: 0644]
drivers/base/regmap/regcache-lzo.c [new file with mode: 0644]
drivers/base/regmap/regcache-rbtree.c [new file with mode: 0644]
drivers/base/regmap/regcache.c [new file with mode: 0644]
drivers/base/regmap/regmap-debugfs.c [new file with mode: 0644]
drivers/base/regmap/regmap-i2c.c
drivers/base/regmap/regmap-spi.c
drivers/base/regmap/regmap.c
drivers/mfd/Kconfig
drivers/mfd/wm831x-core.c
drivers/mfd/wm831x-i2c.c
drivers/mfd/wm831x-spi.c
drivers/mfd/wm8400-core.c
drivers/mfd/wm8994-core.c
include/linux/mfd/wm831x/core.h
include/linux/mfd/wm831x/pdata.h
include/linux/mfd/wm8400-private.h
include/linux/mfd/wm8994/core.h
include/linux/regmap.h
include/trace/events/regmap.h [new file with mode: 0644]

index fabbf6c..2fc6a66 100644 (file)
@@ -4,6 +4,8 @@
 
 config REGMAP
        default y if (REGMAP_I2C || REGMAP_SPI)
+       select LZO_COMPRESS
+       select LZO_DECOMPRESS
        bool
 
 config REGMAP_I2C
index f476f45..0573c8a 100644 (file)
@@ -1,3 +1,4 @@
-obj-$(CONFIG_REGMAP) += regmap.o
+obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o 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
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
new file mode 100644 (file)
index 0000000..348ff02
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Register map access API internal header
+ *
+ * Copyright 2011 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.
+ */
+
+#ifndef _REGMAP_INTERNAL_H
+#define _REGMAP_INTERNAL_H
+
+#include <linux/regmap.h>
+#include <linux/fs.h>
+
+struct regmap;
+struct regcache_ops;
+
+struct regmap_format {
+       size_t buf_size;
+       size_t reg_bytes;
+       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);
+       unsigned int (*parse_val)(void *buf);
+};
+
+struct regmap {
+       struct mutex lock;
+
+       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;
+
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs;
+#endif
+
+       unsigned int max_register;
+       bool (*writeable_reg)(struct device *dev, unsigned int reg);
+       bool (*readable_reg)(struct device *dev, unsigned int reg);
+       bool (*volatile_reg)(struct device *dev, unsigned int reg);
+       bool (*precious_reg)(struct device *dev, unsigned int reg);
+
+       u8 read_flag_mask;
+       u8 write_flag_mask;
+
+       /* regcache specific members */
+       const struct regcache_ops *cache_ops;
+       enum regcache_type cache_type;
+
+       /* number of bytes in reg_defaults_raw */
+       unsigned int cache_size_raw;
+       /* number of bytes per word in reg_defaults_raw */
+       unsigned int cache_word_size;
+       /* number of entries in reg_defaults */
+       unsigned int num_reg_defaults;
+       /* number of entries in reg_defaults_raw */
+       unsigned int num_reg_defaults_raw;
+
+       /* if set, only the cache is modified not the HW */
+       unsigned int cache_only:1;
+       /* if set, only the HW is modified not the cache */
+       unsigned int cache_bypass:1;
+       /* if set, remember to free reg_defaults_raw */
+       unsigned int cache_free:1;
+
+       struct reg_default *reg_defaults;
+       const void *reg_defaults_raw;
+       void *cache;
+};
+
+struct regcache_ops {
+       const char *name;
+       enum regcache_type type;
+       int (*init)(struct regmap *map);
+       int (*exit)(struct regmap *map);
+       int (*read)(struct regmap *map, unsigned int reg, unsigned int *value);
+       int (*write)(struct regmap *map, unsigned int reg, unsigned int value);
+       int (*sync)(struct regmap *map);
+};
+
+bool regmap_writeable(struct regmap *map, unsigned int reg);
+bool regmap_readable(struct regmap *map, unsigned int reg);
+bool regmap_volatile(struct regmap *map, unsigned int reg);
+bool regmap_precious(struct regmap *map, unsigned int reg);
+
+int _regmap_write(struct regmap *map, unsigned int reg,
+                 unsigned int val);
+
+#ifdef CONFIG_DEBUG_FS
+extern void regmap_debugfs_initcall(void);
+extern void regmap_debugfs_init(struct regmap *map);
+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_exit(struct regmap *map) { }
+#endif
+
+/* regcache core declarations */
+int regcache_init(struct regmap *map);
+void regcache_exit(struct regmap *map);
+int regcache_read(struct regmap *map,
+                      unsigned int reg, unsigned int *value);
+int regcache_write(struct regmap *map,
+                       unsigned int reg, unsigned int value);
+int regcache_sync(struct regmap *map);
+
+unsigned int regcache_get_val(const void *base, unsigned int idx,
+                             unsigned int word_size);
+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);
+int regcache_insert_reg(struct regmap *map, unsigned int reg,
+                       unsigned int val);
+
+extern struct regcache_ops regcache_indexed_ops;
+extern struct regcache_ops regcache_rbtree_ops;
+extern struct regcache_ops regcache_lzo_ops;
+
+#endif
diff --git a/drivers/base/regmap/regcache-indexed.c b/drivers/base/regmap/regcache-indexed.c
new file mode 100644 (file)
index 0000000..507731a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Register cache access API - indexed caching support
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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 "internal.h"
+
+static int regcache_indexed_read(struct regmap *map, unsigned int reg,
+                                unsigned int *value)
+{
+       int ret;
+
+       ret = regcache_lookup_reg(map, reg);
+       if (ret >= 0)
+               *value = map->reg_defaults[ret].def;
+
+       return ret;
+}
+
+static int regcache_indexed_write(struct regmap *map, unsigned int reg,
+                                 unsigned int value)
+{
+       int ret;
+
+       ret = regcache_lookup_reg(map, reg);
+       if (ret < 0)
+               return regcache_insert_reg(map, reg, value);
+       map->reg_defaults[ret].def = value;
+       return 0;
+}
+
+static int regcache_indexed_sync(struct regmap *map)
+{
+       unsigned int i;
+       int ret;
+
+       for (i = 0; i < map->num_reg_defaults; i++) {
+               ret = _regmap_write(map, map->reg_defaults[i].reg,
+                                   map->reg_defaults[i].def);
+               if (ret < 0)
+                       return ret;
+               dev_dbg(map->dev, "Synced register %#x, value %#x\n",
+                       map->reg_defaults[i].reg,
+                       map->reg_defaults[i].def);
+       }
+       return 0;
+}
+
+struct regcache_ops regcache_indexed_ops = {
+       .type = REGCACHE_INDEXED,
+       .name = "indexed",
+       .read = regcache_indexed_read,
+       .write = regcache_indexed_write,
+       .sync = regcache_indexed_sync
+};
diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c
new file mode 100644 (file)
index 0000000..066aeec
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * Register cache access API - LZO caching support
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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/lzo.h>
+
+#include "internal.h"
+
+struct regcache_lzo_ctx {
+       void *wmem;
+       void *dst;
+       const void *src;
+       size_t src_len;
+       size_t dst_len;
+       size_t decompressed_size;
+       unsigned long *sync_bmp;
+       int sync_bmp_nbits;
+};
+
+#define LZO_BLOCK_NUM 8
+static int regcache_lzo_block_count(void)
+{
+       return LZO_BLOCK_NUM;
+}
+
+static int regcache_lzo_prepare(struct regcache_lzo_ctx *lzo_ctx)
+{
+       lzo_ctx->wmem = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
+       if (!lzo_ctx->wmem)
+               return -ENOMEM;
+       return 0;
+}
+
+static int regcache_lzo_compress(struct regcache_lzo_ctx *lzo_ctx)
+{
+       size_t compress_size;
+       int ret;
+
+       ret = lzo1x_1_compress(lzo_ctx->src, lzo_ctx->src_len,
+                              lzo_ctx->dst, &compress_size, lzo_ctx->wmem);
+       if (ret != LZO_E_OK || compress_size > lzo_ctx->dst_len)
+               return -EINVAL;
+       lzo_ctx->dst_len = compress_size;
+       return 0;
+}
+
+static int regcache_lzo_decompress(struct regcache_lzo_ctx *lzo_ctx)
+{
+       size_t dst_len;
+       int ret;
+
+       dst_len = lzo_ctx->dst_len;
+       ret = lzo1x_decompress_safe(lzo_ctx->src, lzo_ctx->src_len,
+                                   lzo_ctx->dst, &dst_len);
+       if (ret != LZO_E_OK || dst_len != lzo_ctx->dst_len)
+               return -EINVAL;
+       return 0;
+}
+
+static int regcache_lzo_compress_cache_block(struct regmap *map,
+               struct regcache_lzo_ctx *lzo_ctx)
+{
+       int ret;
+
+       lzo_ctx->dst_len = lzo1x_worst_compress(PAGE_SIZE);
+       lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
+       if (!lzo_ctx->dst) {
+               lzo_ctx->dst_len = 0;
+               return -ENOMEM;
+       }
+
+       ret = regcache_lzo_compress(lzo_ctx);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int regcache_lzo_decompress_cache_block(struct regmap *map,
+               struct regcache_lzo_ctx *lzo_ctx)
+{
+       int ret;
+
+       lzo_ctx->dst_len = lzo_ctx->decompressed_size;
+       lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
+       if (!lzo_ctx->dst) {
+               lzo_ctx->dst_len = 0;
+               return -ENOMEM;
+       }
+
+       ret = regcache_lzo_decompress(lzo_ctx);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static inline int regcache_lzo_get_blkindex(struct regmap *map,
+                                           unsigned int reg)
+{
+       return (reg * map->cache_word_size) /
+               DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count());
+}
+
+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->cache_word_size);
+}
+
+static inline int regcache_lzo_get_blksize(struct regmap *map)
+{
+       return DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count());
+}
+
+static int regcache_lzo_init(struct regmap *map)
+{
+       struct regcache_lzo_ctx **lzo_blocks;
+       size_t bmp_size;
+       int ret, i, blksize, blkcount;
+       const char *p, *end;
+       unsigned long *sync_bmp;
+
+       ret = 0;
+
+       blkcount = regcache_lzo_block_count();
+       map->cache = kzalloc(blkcount * sizeof *lzo_blocks,
+                            GFP_KERNEL);
+       if (!map->cache)
+               return -ENOMEM;
+       lzo_blocks = map->cache;
+
+       /*
+        * allocate a bitmap to be used when syncing the cache with
+        * the hardware.  Each time a register is modified, the corresponding
+        * bit is set in the bitmap, so we know that we have to sync
+        * that register.
+        */
+       bmp_size = map->num_reg_defaults_raw;
+       sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof(long),
+                          GFP_KERNEL);
+       if (!sync_bmp) {
+               ret = -ENOMEM;
+               goto err;
+       }
+       bitmap_zero(sync_bmp, bmp_size);
+
+       /* allocate the lzo blocks and initialize them */
+       for (i = 0; i < blkcount; i++) {
+               lzo_blocks[i] = kzalloc(sizeof **lzo_blocks,
+                                       GFP_KERNEL);
+               if (!lzo_blocks[i]) {
+                       kfree(sync_bmp);
+                       ret = -ENOMEM;
+                       goto err;
+               }
+               lzo_blocks[i]->sync_bmp = sync_bmp;
+               lzo_blocks[i]->sync_bmp_nbits = bmp_size;
+               /* alloc the working space for the compressed block */
+               ret = regcache_lzo_prepare(lzo_blocks[i]);
+               if (ret < 0)
+                       goto err;
+       }
+
+       blksize = regcache_lzo_get_blksize(map);
+       p = map->reg_defaults_raw;
+       end = map->reg_defaults_raw + map->cache_size_raw;
+       /* compress the register map and fill the lzo blocks */
+       for (i = 0; i < blkcount; i++, p += blksize) {
+               lzo_blocks[i]->src = p;
+               if (p + blksize > end)
+                       lzo_blocks[i]->src_len = end - p;
+               else
+                       lzo_blocks[i]->src_len = blksize;
+               ret = regcache_lzo_compress_cache_block(map,
+                                                      lzo_blocks[i]);
+               if (ret < 0)
+                       goto err;
+               lzo_blocks[i]->decompressed_size =
+                       lzo_blocks[i]->src_len;
+       }
+
+       return 0;
+err:
+       regcache_exit(map);
+       return ret;
+}
+
+static int regcache_lzo_exit(struct regmap *map)
+{
+       struct regcache_lzo_ctx **lzo_blocks;
+       int i, blkcount;
+
+       lzo_blocks = map->cache;
+       if (!lzo_blocks)
+               return 0;
+
+       blkcount = regcache_lzo_block_count();
+       /*
+        * the pointer to the bitmap used for syncing the cache
+        * is shared amongst all lzo_blocks.  Ensure it is freed
+        * only once.
+        */
+       if (lzo_blocks[0])
+               kfree(lzo_blocks[0]->sync_bmp);
+       for (i = 0; i < blkcount; i++) {
+               if (lzo_blocks[i]) {
+                       kfree(lzo_blocks[i]->wmem);
+                       kfree(lzo_blocks[i]->dst);
+               }
+               /* each lzo_block is a pointer returned by kmalloc or NULL */
+               kfree(lzo_blocks[i]);
+       }
+       kfree(lzo_blocks);
+       map->cache = NULL;
+       return 0;
+}
+
+static int regcache_lzo_read(struct regmap *map,
+                            unsigned int reg, unsigned int *value)
+{
+       struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
+       int ret, blkindex, blkpos;
+       size_t blksize, tmp_dst_len;
+       void *tmp_dst;
+
+       /* index of the compressed lzo block */
+       blkindex = regcache_lzo_get_blkindex(map, reg);
+       /* register index within the decompressed block */
+       blkpos = regcache_lzo_get_blkpos(map, reg);
+       /* size of the compressed block */
+       blksize = regcache_lzo_get_blksize(map);
+       lzo_blocks = map->cache;
+       lzo_block = lzo_blocks[blkindex];
+
+       /* save the pointer and length of the compressed block */
+       tmp_dst = lzo_block->dst;
+       tmp_dst_len = lzo_block->dst_len;
+
+       /* prepare the source to be the compressed block */
+       lzo_block->src = lzo_block->dst;
+       lzo_block->src_len = lzo_block->dst_len;
+
+       /* decompress the block */
+       ret = regcache_lzo_decompress_cache_block(map, lzo_block);
+       if (ret >= 0)
+               /* fetch the value from the cache */
+               *value = regcache_get_val(lzo_block->dst, blkpos,
+                                         map->cache_word_size);
+
+       kfree(lzo_block->dst);
+       /* restore the pointer and length of the compressed block */
+       lzo_block->dst = tmp_dst;
+       lzo_block->dst_len = tmp_dst_len;
+
+       return ret;
+}
+
+static int regcache_lzo_write(struct regmap *map,
+                             unsigned int reg, unsigned int value)
+{
+       struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
+       int ret, blkindex, blkpos;
+       size_t blksize, tmp_dst_len;
+       void *tmp_dst;
+
+       /* index of the compressed lzo block */
+       blkindex = regcache_lzo_get_blkindex(map, reg);
+       /* register index within the decompressed block */
+       blkpos = regcache_lzo_get_blkpos(map, reg);
+       /* size of the compressed block */
+       blksize = regcache_lzo_get_blksize(map);
+       lzo_blocks = map->cache;
+       lzo_block = lzo_blocks[blkindex];
+
+       /* save the pointer and length of the compressed block */
+       tmp_dst = lzo_block->dst;
+       tmp_dst_len = lzo_block->dst_len;
+
+       /* prepare the source to be the compressed block */
+       lzo_block->src = lzo_block->dst;
+       lzo_block->src_len = lzo_block->dst_len;
+
+       /* decompress the block */
+       ret = regcache_lzo_decompress_cache_block(map, lzo_block);
+       if (ret < 0) {
+               kfree(lzo_block->dst);
+               goto out;
+       }
+
+       /* write the new value to the cache */
+       if (regcache_set_val(lzo_block->dst, blkpos, value,
+                            map->cache_word_size)) {
+               kfree(lzo_block->dst);
+               goto out;
+       }
+
+       /* prepare the source to be the decompressed block */
+       lzo_block->src = lzo_block->dst;
+       lzo_block->src_len = lzo_block->dst_len;
+
+       /* compress the block */
+       ret = regcache_lzo_compress_cache_block(map, lzo_block);
+       if (ret < 0) {
+               kfree(lzo_block->dst);
+               kfree(lzo_block->src);
+               goto out;
+       }
+
+       /* set the bit so we know we have to sync this register */
+       set_bit(reg, lzo_block->sync_bmp);
+       kfree(tmp_dst);
+       kfree(lzo_block->src);
+       return 0;
+out:
+       lzo_block->dst = tmp_dst;
+       lzo_block->dst_len = tmp_dst_len;
+       return ret;
+}
+
+static int regcache_lzo_sync(struct regmap *map)
+{
+       struct regcache_lzo_ctx **lzo_blocks;
+       unsigned int val;
+       int i;
+       int ret;
+
+       lzo_blocks = map->cache;
+       for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) {
+               ret = regcache_read(map, i, &val);
+               if (ret)
+                       return ret;
+               map->cache_bypass = 1;
+               ret = _regmap_write(map, i, val);
+               map->cache_bypass = 0;
+               if (ret)
+                       return ret;
+               dev_dbg(map->dev, "Synced register %#x, value %#x\n",
+                       i, val);
+       }
+
+       return 0;
+}
+
+struct regcache_ops regcache_lzo_ops = {
+       .type = REGCACHE_LZO,
+       .name = "lzo",
+       .init = regcache_lzo_init,
+       .exit = regcache_lzo_exit,
+       .read = regcache_lzo_read,
+       .write = regcache_lzo_write,
+       .sync = regcache_lzo_sync
+};
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c
new file mode 100644 (file)
index 0000000..e314984
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Register cache access API - rbtree caching support
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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/rbtree.h>
+
+#include "internal.h"
+
+static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
+                                unsigned int value);
+
+struct regcache_rbtree_node {
+       /* the actual rbtree node holding this block */
+       struct rb_node node;
+       /* base register handled by this block */
+       unsigned int base_reg;
+       /* block of adjacent registers */
+       void *block;
+       /* number of registers available in the block */
+       unsigned int blklen;
+} __attribute__ ((packed));
+
+struct regcache_rbtree_ctx {
+       struct rb_root root;
+       struct regcache_rbtree_node *cached_rbnode;
+};
+
+static inline void regcache_rbtree_get_base_top_reg(
+       struct regcache_rbtree_node *rbnode,
+       unsigned int *base, unsigned int *top)
+{
+       *base = rbnode->base_reg;
+       *top = rbnode->base_reg + rbnode->blklen - 1;
+}
+
+static unsigned int regcache_rbtree_get_register(
+       struct regcache_rbtree_node *rbnode, unsigned int idx,
+       unsigned int word_size)
+{
+       return regcache_get_val(rbnode->block, idx, word_size);
+}
+
+static void regcache_rbtree_set_register(struct regcache_rbtree_node *rbnode,
+                                        unsigned int idx, unsigned int val,
+                                        unsigned int word_size)
+{
+       regcache_set_val(rbnode->block, idx, val, word_size);
+}
+
+static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
+       unsigned int reg)
+{
+       struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
+       struct rb_node *node;
+       struct regcache_rbtree_node *rbnode;
+       unsigned int base_reg, top_reg;
+
+       rbnode = rbtree_ctx->cached_rbnode;
+       if (rbnode) {
+               regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
+               if (reg >= base_reg && reg <= top_reg)
+                       return rbnode;
+       }
+
+       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);
+               if (reg >= base_reg && reg <= top_reg) {
+                       rbtree_ctx->cached_rbnode = rbnode;
+                       return rbnode;
+               } else if (reg > top_reg) {
+                       node = node->rb_right;
+               } else if (reg < base_reg) {
+                       node = node->rb_left;
+               }
+       }
+
+       return NULL;
+}
+
+static int regcache_rbtree_insert(struct rb_root *root,
+                                 struct regcache_rbtree_node *rbnode)
+{
+       struct rb_node **new, *parent;
+       struct regcache_rbtree_node *rbnode_tmp;
+       unsigned int base_reg_tmp, top_reg_tmp;
+       unsigned int base_reg;
+
+       parent = NULL;
+       new = &root->rb_node;
+       while (*new) {
+               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,
+                                                &top_reg_tmp);
+               /* base register of the rbnode to be added */
+               base_reg = rbnode->base_reg;
+               parent = *new;
+               /* if this register has already been inserted, just return */
+               if (base_reg >= base_reg_tmp &&
+                   base_reg <= top_reg_tmp)
+                       return 0;
+               else if (base_reg > top_reg_tmp)
+                       new = &((*new)->rb_right);
+               else if (base_reg < base_reg_tmp)
+                       new = &((*new)->rb_left);
+       }
+
+       /* insert the node into the rbtree */
+       rb_link_node(&rbnode->node, parent, new);
+       rb_insert_color(&rbnode->node, root);
+
+       return 1;
+}
+
+static int regcache_rbtree_init(struct regmap *map)
+{
+       struct regcache_rbtree_ctx *rbtree_ctx;
+       int i;
+       int ret;
+
+       map->cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
+       if (!map->cache)
+               return -ENOMEM;
+
+       rbtree_ctx = map->cache;
+       rbtree_ctx->root = RB_ROOT;
+       rbtree_ctx->cached_rbnode = NULL;
+
+       for (i = 0; i < map->num_reg_defaults; i++) {
+               ret = regcache_rbtree_write(map,
+                                           map->reg_defaults[i].reg,
+                                           map->reg_defaults[i].def);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       regcache_exit(map);
+       return ret;
+}
+
+static int regcache_rbtree_exit(struct regmap *map)
+{
+       struct rb_node *next;
+       struct regcache_rbtree_ctx *rbtree_ctx;
+       struct regcache_rbtree_node *rbtree_node;
+
+       /* if we've already been called then just return */
+       rbtree_ctx = map->cache;
+       if (!rbtree_ctx)
+               return 0;
+
+       /* free up the rbtree */
+       next = rb_first(&rbtree_ctx->root);
+       while (next) {
+               rbtree_node = rb_entry(next, struct regcache_rbtree_node, node);
+               next = rb_next(&rbtree_node->node);
+               rb_erase(&rbtree_node->node, &rbtree_ctx->root);
+               kfree(rbtree_node->block);
+               kfree(rbtree_node);
+       }
+
+       /* release the resources */
+       kfree(map->cache);
+       map->cache = NULL;
+
+       return 0;
+}
+
+static int regcache_rbtree_read(struct regmap *map,
+                               unsigned int reg, unsigned int *value)
+{
+       struct regcache_rbtree_node *rbnode;
+       unsigned int reg_tmp;
+
+       rbnode = regcache_rbtree_lookup(map, reg);
+       if (rbnode) {
+               reg_tmp = reg - rbnode->base_reg;
+               *value = regcache_rbtree_get_register(rbnode, reg_tmp,
+                                                     map->cache_word_size);
+       } else {
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+
+static int regcache_rbtree_insert_to_block(struct regcache_rbtree_node *rbnode,
+                                          unsigned int pos, unsigned int reg,
+                                          unsigned int value, unsigned int word_size)
+{
+       u8 *blk;
+
+       blk = krealloc(rbnode->block,
+                      (rbnode->blklen + 1) * word_size, GFP_KERNEL);
+       if (!blk)
+               return -ENOMEM;
+
+       /* insert the register value in the correct place in the rbnode block */
+       memmove(blk + (pos + 1) * word_size,
+               blk + pos * word_size,
+               (rbnode->blklen - pos) * word_size);
+
+       /* update the rbnode block, its size and the base register */
+       rbnode->block = blk;
+       rbnode->blklen++;
+       if (!pos)
+               rbnode->base_reg = reg;
+
+       regcache_rbtree_set_register(rbnode, pos, value, word_size);
+       return 0;
+}
+
+static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
+                                unsigned int value)
+{
+       struct regcache_rbtree_ctx *rbtree_ctx;
+       struct regcache_rbtree_node *rbnode, *rbnode_tmp;
+       struct rb_node *node;
+       unsigned int val;
+       unsigned int reg_tmp;
+       unsigned int pos;
+       int i;
+       int ret;
+
+       rbtree_ctx = map->cache;
+       /* if we can't locate it in the cached rbnode we'll have
+        * to traverse the rbtree looking for it.
+        */
+       rbnode = regcache_rbtree_lookup(map, reg);
+       if (rbnode) {
+               reg_tmp = reg - rbnode->base_reg;
+               val = regcache_rbtree_get_register(rbnode, reg_tmp,
+                                                  map->cache_word_size);
+               if (val == value)
+                       return 0;
+               regcache_rbtree_set_register(rbnode, reg_tmp, value,
+                                            map->cache_word_size);
+       } else {
+               /* 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);
+                       for (i = 0; i < rbnode_tmp->blklen; i++) {
+                               reg_tmp = rbnode_tmp->base_reg + i;
+                               if (abs(reg_tmp - reg) != 1)
+                                       continue;
+                               /* decide where in the block to place our register */
+                               if (reg_tmp + 1 == reg)
+                                       pos = i + 1;
+                               else
+                                       pos = i;
+                               ret = regcache_rbtree_insert_to_block(rbnode_tmp, pos,
+                                                                     reg, value,
+                                                                     map->cache_word_size);
+                               if (ret)
+                                       return ret;
+                               rbtree_ctx->cached_rbnode = rbnode_tmp;
+                               return 0;
+                       }
+               }
+               /* we did not manage to find a place to insert it in an existing
+                * block so create a new rbnode with a single register in its block.
+                * This block will get populated further if any other adjacent
+                * registers get modified in the future.
+                */
+               rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL);
+               if (!rbnode)
+                       return -ENOMEM;
+               rbnode->blklen = 1;
+               rbnode->base_reg = reg;
+               rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
+                                       GFP_KERNEL);
+               if (!rbnode->block) {
+                       kfree(rbnode);
+                       return -ENOMEM;
+               }
+               regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size);
+               regcache_rbtree_insert(&rbtree_ctx->root, rbnode);
+               rbtree_ctx->cached_rbnode = rbnode;
+       }
+
+       return 0;
+}
+
+static int regcache_rbtree_sync(struct regmap *map)
+{
+       struct regcache_rbtree_ctx *rbtree_ctx;
+       struct rb_node *node;
+       struct regcache_rbtree_node *rbnode;
+       unsigned int regtmp;
+       unsigned int val;
+       int ret;
+       int i;
+
+       rbtree_ctx = map->cache;
+       for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
+               rbnode = rb_entry(node, struct regcache_rbtree_node, node);
+               for (i = 0; i < rbnode->blklen; i++) {
+                       regtmp = rbnode->base_reg + i;
+                       val = regcache_rbtree_get_register(rbnode, i,
+                                                          map->cache_word_size);
+
+                       /* Is this the hardware default?  If so skip. */
+                       ret = regcache_lookup_reg(map, i);
+                       if (ret > 0 && val == map->reg_defaults[ret].def)
+                               continue;
+
+                       map->cache_bypass = 1;
+                       ret = _regmap_write(map, regtmp, val);
+                       map->cache_bypass = 0;
+                       if (ret)
+                               return ret;
+                       dev_dbg(map->dev, "Synced register %#x, value %#x\n",
+                               regtmp, val);
+               }
+       }
+
+       return 0;
+}
+
+struct regcache_ops regcache_rbtree_ops = {
+       .type = REGCACHE_RBTREE,
+       .name = "rbtree",
+       .init = regcache_rbtree_init,
+       .exit = regcache_rbtree_exit,
+       .read = regcache_rbtree_read,
+       .write = regcache_rbtree_write,
+       .sync = regcache_rbtree_sync
+};
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
new file mode 100644 (file)
index 0000000..afcfef8
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Register cache access API
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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 <trace/events/regmap.h>
+#include <linux/bsearch.h>
+#include <linux/sort.h>
+
+#include "internal.h"
+
+static const struct regcache_ops *cache_types[] = {
+       &regcache_indexed_ops,
+       &regcache_rbtree_ops,
+       &regcache_lzo_ops,
+};
+
+static int regcache_hw_init(struct regmap *map)
+{
+       int i, j;
+       int ret;
+       int count;
+       unsigned int val;
+       void *tmp_buf;
+
+       if (!map->num_reg_defaults_raw)
+               return -EINVAL;
+
+       if (!map->reg_defaults_raw) {
+               dev_warn(map->dev, "No cache defaults, reading back from HW\n");
+               tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL);
+               if (!tmp_buf)
+                       return -EINVAL;
+               ret = regmap_bulk_read(map, 0, tmp_buf,
+                                      map->num_reg_defaults_raw);
+               if (ret < 0) {
+                       kfree(tmp_buf);
+                       return ret;
+               }
+               map->reg_defaults_raw = tmp_buf;
+               map->cache_free = 1;
+       }
+
+       /* calculate the size of reg_defaults */
+       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 (!val)
+                       continue;
+               count++;
+       }
+
+       map->reg_defaults = kmalloc(count * sizeof(struct reg_default),
+                                     GFP_KERNEL);
+       if (!map->reg_defaults)
+               return -ENOMEM;
+
+       /* fill the reg_defaults */
+       map->num_reg_defaults = count;
+       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 (!val)
+                       continue;
+               map->reg_defaults[j].reg = i;
+               map->reg_defaults[j].def = val;
+               j++;
+       }
+
+       return 0;
+}
+
+int regcache_init(struct regmap *map)
+{
+       int ret;
+       int i;
+       void *tmp_buf;
+
+       if (map->cache_type == REGCACHE_NONE) {
+               map->cache_bypass = true;
+               return 0;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(cache_types); i++)
+               if (cache_types[i]->type == map->cache_type)
+                       break;
+
+       if (i == ARRAY_SIZE(cache_types)) {
+               dev_err(map->dev, "Could not match compress type: %d\n",
+                       map->cache_type);
+               return -EINVAL;
+       }
+
+       map->cache = NULL;
+       map->cache_ops = cache_types[i];
+
+       if (!map->cache_ops->read ||
+           !map->cache_ops->write ||
+           !map->cache_ops->name)
+               return -EINVAL;
+
+       /* We still need to ensure that the reg_defaults
+        * won't vanish from under us.  We'll need to make
+        * a copy of it.
+        */
+       if (map->reg_defaults) {
+               if (!map->num_reg_defaults)
+                       return -EINVAL;
+               tmp_buf = kmemdup(map->reg_defaults, map->num_reg_defaults *
+                                 sizeof(struct reg_default), GFP_KERNEL);
+               if (!tmp_buf)
+                       return -ENOMEM;
+               map->reg_defaults = tmp_buf;
+       } else if (map->num_reg_defaults_raw) {
+               /* Some devices such as PMICs don't have cache defaults,
+                * we cope with this by reading back the HW registers and
+                * crafting the cache defaults by hand.
+                */
+               ret = regcache_hw_init(map);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (!map->max_register)
+               map->max_register = map->num_reg_defaults_raw;
+
+       if (map->cache_ops->init) {
+               dev_dbg(map->dev, "Initializing %s cache\n",
+                       map->cache_ops->name);
+               return map->cache_ops->init(map);
+       }
+       return 0;
+}
+
+void regcache_exit(struct regmap *map)
+{
+       if (map->cache_type == REGCACHE_NONE)
+               return;
+
+       BUG_ON(!map->cache_ops);
+
+       kfree(map->reg_defaults);
+       if (map->cache_free)
+               kfree(map->reg_defaults_raw);
+
+       if (map->cache_ops->exit) {
+               dev_dbg(map->dev, "Destroying %s cache\n",
+                       map->cache_ops->name);
+               map->cache_ops->exit(map);
+       }
+}
+
+/**
+ * regcache_read: Fetch the value of a given register from the cache.
+ *
+ * @map: map to configure.
+ * @reg: The register index.
+ * @value: The value to be returned.
+ *
+ * Return a negative value on failure, 0 on success.
+ */
+int regcache_read(struct regmap *map,
+                 unsigned int reg, unsigned int *value)
+{
+       if (map->cache_type == REGCACHE_NONE)
+               return -ENOSYS;
+
+       BUG_ON(!map->cache_ops);
+
+       if (!regmap_readable(map, reg))
+               return -EIO;
+
+       if (!regmap_volatile(map, reg))
+               return map->cache_ops->read(map, reg, value);
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(regcache_read);
+
+/**
+ * regcache_write: Set the value of a given register in the cache.
+ *
+ * @map: map to configure.
+ * @reg: The register index.
+ * @value: The new register value.
+ *
+ * Return a negative value on failure, 0 on success.
+ */
+int regcache_write(struct regmap *map,
+                  unsigned int reg, unsigned int value)
+{
+       if (map->cache_type == REGCACHE_NONE)
+               return 0;
+
+       BUG_ON(!map->cache_ops);
+
+       if (!regmap_writeable(map, reg))
+               return -EIO;
+
+       if (!regmap_volatile(map, reg))
+               return map->cache_ops->write(map, reg, value);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(regcache_write);
+
+/**
+ * regcache_sync: Sync the register cache with the hardware.
+ *
+ * @map: map to configure.
+ *
+ * Any registers that should not be synced should be marked as
+ * volatile.  In general drivers can choose not to use the provided
+ * syncing functionality if they so require.
+ *
+ * Return a negative value on failure, 0 on success.
+ */
+int regcache_sync(struct regmap *map)
+{
+       int ret = 0;
+       unsigned int val;
+       unsigned int i;
+       const char *name;
+       unsigned int bypass;
+
+       BUG_ON(!map->cache_ops);
+
+       mutex_lock(&map->lock);
+       /* Remember the initial bypass state */
+       bypass = map->cache_bypass;
+       dev_dbg(map->dev, "Syncing %s cache\n",
+               map->cache_ops->name);
+       name = map->cache_ops->name;
+       trace_regcache_sync(map->dev, name, "start");
+       if (map->cache_ops->sync) {
+               ret = map->cache_ops->sync(map);
+       } else {
+               for (i = 0; i < map->num_reg_defaults; i++) {
+                       ret = regcache_read(map, i, &val);
+                       if (ret < 0)
+                               goto out;
+                       map->cache_bypass = 1;
+                       ret = _regmap_write(map, i, val);
+                       map->cache_bypass = 0;
+                       if (ret < 0)
+                               goto out;
+                       dev_dbg(map->dev, "Synced register %#x, value %#x\n",
+                               map->reg_defaults[i].reg,
+                               map->reg_defaults[i].def);
+               }
+
+       }
+out:
+       trace_regcache_sync(map->dev, name, "stop");
+       /* Restore the bypass state */
+       map->cache_bypass = bypass;
+       mutex_unlock(&map->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regcache_sync);
+
+/**
+ * regcache_cache_only: Put a register map into cache only mode
+ *
+ * @map: map to configure
+ * @cache_only: flag if changes should be written to the hardware
+ *
+ * When a register map is marked as cache only writes to the register
+ * map API will only update the register cache, they will not cause
+ * any hardware changes.  This is useful for allowing portions of
+ * drivers to act as though the device were functioning as normal when
+ * it is disabled for power saving reasons.
+ */
+void regcache_cache_only(struct regmap *map, bool enable)
+{
+       mutex_lock(&map->lock);
+       WARN_ON(map->cache_bypass && enable);
+       map->cache_only = enable;
+       mutex_unlock(&map->lock);
+}
+EXPORT_SYMBOL_GPL(regcache_cache_only);
+
+/**
+ * regcache_cache_bypass: Put a register map into cache bypass mode
+ *
+ * @map: map to configure
+ * @cache_bypass: flag if changes should not be written to the hardware
+ *
+ * When a register map is marked with the cache bypass option, writes
+ * to the register map API will only update the hardware and not the
+ * the cache directly.  This is useful when syncing the cache back to
+ * the hardware.
+ */
+void regcache_cache_bypass(struct regmap *map, bool enable)
+{
+       mutex_lock(&map->lock);
+       WARN_ON(map->cache_only && enable);
+       map->cache_bypass = enable;
+       mutex_unlock(&map->lock);
+}
+EXPORT_SYMBOL_GPL(regcache_cache_bypass);
+
+bool regcache_set_val(void *base, unsigned int idx,
+                     unsigned int val, unsigned int word_size)
+{
+       switch (word_size) {
+       case 1: {
+               u8 *cache = base;
+               if (cache[idx] == val)
+                       return true;
+               cache[idx] = val;
+               break;
+       }
+       case 2: {
+               u16 *cache = base;
+               if (cache[idx] == val)
+                       return true;
+               cache[idx] = val;
+               break;
+       }
+       default:
+               BUG();
+       }
+       /* unreachable */
+       return false;
+}
+
+unsigned int regcache_get_val(const void *base, unsigned int idx,
+                             unsigned int word_size)
+{
+       if (!base)
+               return -EINVAL;
+
+       switch (word_size) {
+       case 1: {
+               const u8 *cache = base;
+               return cache[idx];
+       }
+       case 2: {
+               const u16 *cache = base;
+               return cache[idx];
+       }
+       default:
+               BUG();
+       }
+       /* unreachable */
+       return -1;
+}
+
+static int regcache_default_cmp(const void *a, const void *b)
+{
+       const struct reg_default *_a = a;
+       const struct reg_default *_b = b;
+
+       return _a->reg - _b->reg;
+}
+
+int regcache_lookup_reg(struct regmap *map, unsigned int reg)
+{
+       struct reg_default key;
+       struct reg_default *r;
+
+       key.reg = reg;
+       key.def = 0;
+
+       r = bsearch(&key, map->reg_defaults, map->num_reg_defaults,
+                   sizeof(struct reg_default), regcache_default_cmp);
+
+       if (r)
+               return r - map->reg_defaults;
+       else
+               return -ENOENT;
+}
+
+int regcache_insert_reg(struct regmap *map, unsigned int reg,
+                       unsigned int val)
+{
+       void *tmp;
+
+       tmp = krealloc(map->reg_defaults,
+                      (map->num_reg_defaults + 1) * sizeof(struct reg_default),
+                      GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+       map->reg_defaults = tmp;
+       map->num_reg_defaults++;
+       map->reg_defaults[map->num_reg_defaults - 1].reg = reg;
+       map->reg_defaults[map->num_reg_defaults - 1].def = val;
+       sort(map->reg_defaults, map->num_reg_defaults,
+            sizeof(struct reg_default), regcache_default_cmp, NULL);
+       return 0;
+}
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
new file mode 100644 (file)
index 0000000..6f39747
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Register map access API - debugfs
+ *
+ * Copyright 2011 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/module.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+#include "internal.h"
+
+static struct dentry *regmap_debugfs_root;
+
+/* Calculate the length of a fixed format  */
+static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size)
+{
+       snprintf(buf, buf_size, "%x", max_val);
+       return strlen(buf);
+}
+
+static int regmap_open_file(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       int reg_len, val_len, tot_len;
+       size_t buf_pos = 0;
+       loff_t p = 0;
+       ssize_t ret;
+       int i;
+       struct regmap *map = file->private_data;
+       char *buf;
+       unsigned int val;
+
+       if (*ppos < 0 || !count)
+               return -EINVAL;
+
+       buf = kmalloc(count, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       /* Calculate the length of a fixed format  */
+       reg_len = regmap_calc_reg_len(map->max_register, buf, count);
+       val_len = 2 * map->format.val_bytes;
+       tot_len = reg_len + val_len + 3;      /* : \n */
+
+       for (i = 0; i < map->max_register + 1; i++) {
+               if (!regmap_readable(map, i))
+                       continue;
+
+               if (regmap_precious(map, i))
+                       continue;
+
+               /* If we're in the region the user is trying to read */
+               if (p >= *ppos) {
+                       /* ...but not beyond it */
+                       if (buf_pos >= count - 1 - tot_len)
+                               break;
+
+                       /* Format the register */
+                       snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
+                                reg_len, i);
+                       buf_pos += reg_len + 2;
+
+                       /* Format the value, write all X if we can't read */
+                       ret = regmap_read(map, i, &val);
+                       if (ret == 0)
+                               snprintf(buf + buf_pos, count - buf_pos,
+                                        "%.*x", val_len, val);
+                       else
+                               memset(buf + buf_pos, 'X', val_len);
+                       buf_pos += 2 * map->format.val_bytes;
+
+                       buf[buf_pos++] = '\n';
+               }
+               p += tot_len;
+       }
+
+       ret = buf_pos;
+
+       if (copy_to_user(user_buf, buf, buf_pos)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       *ppos += buf_pos;
+
+out:
+       kfree(buf);
+       return ret;
+}
+
+static const struct file_operations regmap_map_fops = {
+       .open = regmap_open_file,
+       .read = regmap_map_read_file,
+       .llseek = default_llseek,
+};
+
+static ssize_t regmap_access_read_file(struct file *file,
+                                      char __user *user_buf, size_t count,
+                                      loff_t *ppos)
+{
+       int reg_len, tot_len;
+       size_t buf_pos = 0;
+       loff_t p = 0;
+       ssize_t ret;
+       int i;
+       struct regmap *map = file->private_data;
+       char *buf;
+
+       if (*ppos < 0 || !count)
+               return -EINVAL;
+
+       buf = kmalloc(count, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       /* Calculate the length of a fixed format  */
+       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++) {
+               /* Ignore registers which are neither readable nor writable */
+               if (!regmap_readable(map, i) && !regmap_writeable(map, i))
+                       continue;
+
+               /* If we're in the region the user is trying to read */
+               if (p >= *ppos) {
+                       /* ...but not beyond it */
+                       if (buf_pos >= count - 1 - tot_len)
+                               break;
+
+                       /* Format the register */
+                       snprintf(buf + buf_pos, count - buf_pos,
+                                "%.*x: %c %c %c %c\n",
+                                reg_len, i,
+                                regmap_readable(map, i) ? 'y' : 'n',
+                                regmap_writeable(map, i) ? 'y' : 'n',
+                                regmap_volatile(map, i) ? 'y' : 'n',
+                                regmap_precious(map, i) ? 'y' : 'n');
+
+                       buf_pos += tot_len;
+               }
+               p += tot_len;
+       }
+
+       ret = buf_pos;
+
+       if (copy_to_user(user_buf, buf, buf_pos)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       *ppos += buf_pos;
+
+out:
+       kfree(buf);
+       return ret;
+}
+
+static const struct file_operations regmap_access_fops = {
+       .open = regmap_open_file,
+       .read = regmap_access_read_file,
+       .llseek = default_llseek,
+};
+
+void regmap_debugfs_init(struct regmap *map)
+{
+       map->debugfs = debugfs_create_dir(dev_name(map->dev),
+                                         regmap_debugfs_root);
+       if (!map->debugfs) {
+               dev_warn(map->dev, "Failed to create debugfs directory\n");
+               return;
+       }
+
+       if (map->max_register) {
+               debugfs_create_file("registers", 0400, map->debugfs,
+                                   map, &regmap_map_fops);
+               debugfs_create_file("access", 0400, map->debugfs,
+                                   map, &regmap_access_fops);
+       }
+}
+
+void regmap_debugfs_exit(struct regmap *map)
+{
+       debugfs_remove_recursive(map->debugfs);
+}
+
+void regmap_debugfs_initcall(void)
+{
+       regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
+       if (!regmap_debugfs_root) {
+               pr_warn("regmap: Failed to create debugfs root\n");
+               return;
+       }
+}
index c4f7a45..38621ec 100644 (file)
@@ -90,11 +90,9 @@ static int regmap_i2c_read(struct device *dev,
 }
 
 static struct regmap_bus regmap_i2c = {
-       .type = &i2c_bus_type,
        .write = regmap_i2c_write,
        .gather_write = regmap_i2c_gather_write,
        .read = regmap_i2c_read,
-       .owner = THIS_MODULE,
 };
 
 /**
index f839694..2560658 100644 (file)
@@ -48,11 +48,9 @@ static int regmap_spi_read(struct device *dev,
 }
 
 static struct regmap_bus regmap_spi = {
-       .type = &spi_bus_type,
        .write = regmap_spi_write,
        .gather_write = regmap_spi_gather_write,
        .read = regmap_spi_read,
-       .owner = THIS_MODULE,
        .read_flag_mask = 0x80,
 };
 
index 20663f8..bf441db 100644 (file)
 #include <linux/mutex.h>
 #include <linux/err.h>
 
-#include <linux/regmap.h>
-
-struct regmap;
-
-struct regmap_format {
-       size_t buf_size;
-       size_t reg_bytes;
-       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);
-       unsigned int (*parse_val)(void *buf);
-};
-
-struct regmap {
-       struct mutex lock;
-
-       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;
-};
+#define CREATE_TRACE_POINTS
+#include <trace/events/regmap.h>
+
+#include "internal.h"
+
+bool regmap_writeable(struct regmap *map, unsigned int reg)
+{
+       if (map->max_register && reg > map->max_register)
+               return false;
+
+       if (map->writeable_reg)
+               return map->writeable_reg(map->dev, reg);
+
+       return true;
+}
+
+bool regmap_readable(struct regmap *map, unsigned int reg)
+{
+       if (map->max_register && reg > map->max_register)
+               return false;
+
+       if (map->readable_reg)
+               return map->readable_reg(map->dev, reg);
+
+       return true;
+}
+
+bool regmap_volatile(struct regmap *map, unsigned int reg)
+{
+       if (map->max_register && reg > map->max_register)
+               return false;
+
+       if (map->volatile_reg)
+               return map->volatile_reg(map->dev, reg);
+
+       return true;
+}
+
+bool regmap_precious(struct regmap *map, unsigned int reg)
+{
+       if (map->max_register && reg > map->max_register)
+               return false;
+
+       if (map->precious_reg)
+               return map->precious_reg(map->dev, reg);
+
+       return false;
+}
 
 static void regmap_format_4_12_write(struct regmap *map,
                                     unsigned int reg, unsigned int val)
@@ -116,6 +141,25 @@ struct regmap *regmap_init(struct device *dev,
        map->format.val_bytes = config->val_bits / 8;
        map->dev = dev;
        map->bus = bus;
+       map->max_register = config->max_register;
+       map->writeable_reg = config->writeable_reg;
+       map->readable_reg = config->readable_reg;
+       map->volatile_reg = config->volatile_reg;
+       map->precious_reg = config->precious_reg;
+       map->cache_type = config->cache_type;
+       map->reg_defaults = config->reg_defaults;
+       map->num_reg_defaults = config->num_reg_defaults;
+       map->num_reg_defaults_raw = config->num_reg_defaults_raw;
+       map->reg_defaults_raw = config->reg_defaults_raw;
+       map->cache_size_raw = (config->val_bits / 8) * config->num_reg_defaults_raw;
+       map->cache_word_size = config->val_bits / 8;
+
+       if (config->read_flag_mask || config->write_flag_mask) {
+               map->read_flag_mask = config->read_flag_mask;
+               map->write_flag_mask = config->write_flag_mask;
+       } else {
+               map->read_flag_mask = bus->read_flag_mask;
+       }
 
        switch (config->reg_bits) {
        case 4:
@@ -171,6 +215,12 @@ struct regmap *regmap_init(struct device *dev,
                goto err_map;
        }
 
+       ret = regcache_init(map);
+       if (ret < 0)
+               goto err_map;
+
+       regmap_debugfs_init(map);
+
        return map;
 
 err_map:
@@ -185,6 +235,8 @@ EXPORT_SYMBOL_GPL(regmap_init);
  */
 void regmap_exit(struct regmap *map)
 {
+       regcache_exit(map);
+       regmap_debugfs_exit(map);
        kfree(map->work_buf);
        kfree(map);
 }
@@ -193,19 +245,38 @@ EXPORT_SYMBOL_GPL(regmap_exit);
 static int _regmap_raw_write(struct regmap *map, unsigned int reg,
                             const void *val, size_t val_len)
 {
+       u8 *u8 = map->work_buf;
        void *buf;
        int ret = -ENOTSUPP;
        size_t len;
+       int i;
+
+       /* 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))
+                               return -EINVAL;
 
        map->format.format_reg(map->work_buf, reg);
 
-       /* Try to do a gather write if we can */
-       if (map->bus->gather_write)
+       u8[0] |= map->write_flag_mask;
+
+       trace_regmap_hw_write_start(map->dev, reg,
+                                   val_len / map->format.val_bytes);
+
+       /* If we're doing a single register write we can probably just
+        * send the work_buf directly, otherwise try to do a gather
+        * write.
+        */
+       if (val == map->work_buf + map->format.reg_bytes)
+               ret = map->bus->write(map->dev, map->work_buf,
+                                     map->format.reg_bytes + val_len);
+       else if (map->bus->gather_write)
                ret = map->bus->gather_write(map->dev, map->work_buf,
                                             map->format.reg_bytes,
                                             val, val_len);
 
-       /* Otherwise fall back on linearising by hand. */
+       /* If that didn't work fall back on linearising by hand. */
        if (ret == -ENOTSUPP) {
                len = map->format.reg_bytes + val_len;
                buf = kmalloc(len, GFP_KERNEL);
@@ -219,19 +290,39 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
                kfree(buf);
        }
 
+       trace_regmap_hw_write_done(map->dev, reg,
+                                  val_len / map->format.val_bytes);
+
        return ret;
 }
 
-static int _regmap_write(struct regmap *map, unsigned int reg,
-                        unsigned int val)
+int _regmap_write(struct regmap *map, unsigned int reg,
+                 unsigned int val)
 {
+       int ret;
        BUG_ON(!map->format.format_write && !map->format.format_val);
 
+       if (!map->cache_bypass) {
+               ret = regcache_write(map, reg, val);
+               if (ret != 0)
+                       return ret;
+               if (map->cache_only)
+                       return 0;
+       }
+
+       trace_regmap_reg_write(map->dev, reg, val);
+
        if (map->format.format_write) {
                map->format.format_write(map, reg, val);
 
-               return map->bus->write(map->dev, map->work_buf,
-                                      map->format.buf_size);
+               trace_regmap_hw_write_start(map->dev, reg, 1);
+
+               ret = map->bus->write(map->dev, map->work_buf,
+                                     map->format.buf_size);
+
+               trace_regmap_hw_write_done(map->dev, reg, 1);
+
+               return ret;
        } else {
                map->format.format_val(map->work_buf + map->format.reg_bytes,
                                       val);
@@ -286,6 +377,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
 {
        int ret;
 
+       WARN_ON(map->cache_type != REGCACHE_NONE);
+
        mutex_lock(&map->lock);
 
        ret = _regmap_raw_write(map, reg, val, val_len);
@@ -305,20 +398,23 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
        map->format.format_reg(map->work_buf, reg);
 
        /*
-        * Some buses flag reads by setting the high bits in the
+        * Some buses or devices flag reads by setting the high bits in the
         * register addresss; since it's always the high bits for all
         * current formats we can do this here rather than in
         * formatting.  This may break if we get interesting formats.
         */
-       if (map->bus->read_flag_mask)
-               u8[0] |= map->bus->read_flag_mask;
+       u8[0] |= map->read_flag_mask;
+
+       trace_regmap_hw_read_start(map->dev, reg,
+                                  val_len / map->format.val_bytes);
 
        ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes,
                             val, val_len);
-       if (ret != 0)
-               return ret;
 
-       return 0;
+       trace_regmap_hw_read_done(map->dev, reg,
+                                 val_len / map->format.val_bytes);
+
+       return ret;
 }
 
 static int _regmap_read(struct regmap *map, unsigned int reg,
@@ -329,9 +425,20 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
        if (!map->format.parse_val)
                return -EINVAL;
 
+       if (!map->cache_bypass) {
+               ret = regcache_read(map, reg, val);
+               if (ret == 0)
+                       return 0;
+       }
+
+       if (map->cache_only)
+               return -EBUSY;
+
        ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
-       if (ret == 0)
+       if (ret == 0) {
                *val = map->format.parse_val(map->work_buf);
+               trace_regmap_reg_read(map->dev, reg, *val);
+       }
 
        return ret;
 }
@@ -375,6 +482,14 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
                    size_t val_len)
 {
        int ret;
+       int i;
+       bool vol = true;
+
+       for (i = 0; i < val_len / map->format.val_bytes; i++)
+               if (!regmap_volatile(map, reg + i))
+                       vol = false;
+
+       WARN_ON(!vol && map->cache_type != REGCACHE_NONE);
 
        mutex_lock(&map->lock);
 
@@ -402,16 +517,30 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
 {
        int ret, i;
        size_t val_bytes = map->format.val_bytes;
+       bool vol = true;
 
        if (!map->format.parse_val)
                return -EINVAL;
 
-       ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
-       if (ret != 0)
-               return ret;
+       /* Is this a block of volatile registers? */
+       for (i = 0; i < val_count; i++)
+               if (!regmap_volatile(map, reg + i))
+                       vol = false;
+
+       if (vol || map->cache_type == REGCACHE_NONE) {
+               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);
+               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++) {
+                       ret = regmap_read(map, reg + i, val + (i * val_bytes));
+                       if (ret != 0)
+                               return ret;
+               }
+       }
 
        return 0;
 }
@@ -450,3 +579,11 @@ out:
        return ret;
 }
 EXPORT_SYMBOL_GPL(regmap_update_bits);
+
+static int __init regmap_initcall(void)
+{
+       regmap_debugfs_initcall();
+
+       return 0;
+}
+postcore_initcall(regmap_initcall);
index 21574bd..a67adcb 100644 (file)
@@ -404,6 +404,7 @@ config MFD_WM831X_I2C
        bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C"
        select MFD_CORE
        select MFD_WM831X
+       select REGMAP_I2C
        depends on I2C=y && GENERIC_HARDIRQS
        help
          Support for the Wolfson Microelecronics WM831x and WM832x PMICs
@@ -415,6 +416,7 @@ config MFD_WM831X_SPI
        bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI"
        select MFD_CORE
        select MFD_WM831X
+       select REGMAP_SPI
        depends on SPI_MASTER && GENERIC_HARDIRQS
        help
          Support for the Wolfson Microelecronics WM831x and WM832x PMICs
@@ -488,6 +490,7 @@ config MFD_WM8350_I2C
 config MFD_WM8994
        bool "Support Wolfson Microelectronics WM8994"
        select MFD_CORE
+       select REGMAP_I2C
        depends on I2C=y && GENERIC_HARDIRQS
        help
          The WM8994 is a highly integrated hi-fi CODEC designed for
index 282e76a..0a2b8d4 100644 (file)
 #include <linux/delay.h>
 #include <linux/mfd/core.h>
 #include <linux/slab.h>
+#include <linux/err.h>
 
 #include <linux/mfd/wm831x/core.h>
 #include <linux/mfd/wm831x/pdata.h>
 #include <linux/mfd/wm831x/irq.h>
 #include <linux/mfd/wm831x/auxadc.h>
 #include <linux/mfd/wm831x/otp.h>
+#include <linux/mfd/wm831x/pmu.h>
 #include <linux/mfd/wm831x/regulator.h>
 
 /* Current settings - values are 2*2^(reg_val/4) microamps.  These are
@@ -160,27 +162,350 @@ int wm831x_reg_unlock(struct wm831x *wm831x)
 }
 EXPORT_SYMBOL_GPL(wm831x_reg_unlock);
 
-static int wm831x_read(struct wm831x *wm831x, unsigned short reg,
-                      int bytes, void *dest)
+static bool wm831x_reg_readable(struct device *dev, unsigned int reg)
 {
-       int ret, i;
-       u16 *buf = dest;
-
-       BUG_ON(bytes % 2);
-       BUG_ON(bytes <= 0);
+       switch (reg) {
+       case WM831X_RESET_ID:
+       case WM831X_REVISION:
+       case WM831X_PARENT_ID:
+       case WM831X_SYSVDD_CONTROL:
+       case WM831X_THERMAL_MONITORING:
+       case WM831X_POWER_STATE:
+       case WM831X_WATCHDOG:
+       case WM831X_ON_PIN_CONTROL:
+       case WM831X_RESET_CONTROL:
+       case WM831X_CONTROL_INTERFACE:
+       case WM831X_SECURITY_KEY:
+       case WM831X_SOFTWARE_SCRATCH:
+       case WM831X_OTP_CONTROL:
+       case WM831X_GPIO_LEVEL:
+       case WM831X_SYSTEM_STATUS:
+       case WM831X_ON_SOURCE:
+       case WM831X_OFF_SOURCE:
+       case WM831X_SYSTEM_INTERRUPTS:
+       case WM831X_INTERRUPT_STATUS_1:
+       case WM831X_INTERRUPT_STATUS_2:
+       case WM831X_INTERRUPT_STATUS_3:
+       case WM831X_INTERRUPT_STATUS_4:
+       case WM831X_INTERRUPT_STATUS_5:
+       case WM831X_IRQ_CONFIG:
+       case WM831X_SYSTEM_INTERRUPTS_MASK:
+       case WM831X_INTERRUPT_STATUS_1_MASK:
+       case WM831X_INTERRUPT_STATUS_2_MASK:
+       case WM831X_INTERRUPT_STATUS_3_MASK:
+       case WM831X_INTERRUPT_STATUS_4_MASK:
+       case WM831X_INTERRUPT_STATUS_5_MASK:
+       case WM831X_RTC_WRITE_COUNTER:
+       case WM831X_RTC_TIME_1:
+       case WM831X_RTC_TIME_2:
+       case WM831X_RTC_ALARM_1:
+       case WM831X_RTC_ALARM_2:
+       case WM831X_RTC_CONTROL:
+       case WM831X_RTC_TRIM:
+       case WM831X_TOUCH_CONTROL_1:
+       case WM831X_TOUCH_CONTROL_2:
+       case WM831X_TOUCH_DATA_X:
+       case WM831X_TOUCH_DATA_Y:
+       case WM831X_TOUCH_DATA_Z:
+       case WM831X_AUXADC_DATA:
+       case WM831X_AUXADC_CONTROL:
+       case WM831X_AUXADC_SOURCE:
+       case WM831X_COMPARATOR_CONTROL:
+       case WM831X_COMPARATOR_1:
+       case WM831X_COMPARATOR_2:
+       case WM831X_COMPARATOR_3:
+       case WM831X_COMPARATOR_4:
+       case WM831X_GPIO1_CONTROL:
+       case WM831X_GPIO2_CONTROL:
+       case WM831X_GPIO3_CONTROL:
+       case WM831X_GPIO4_CONTROL:
+       case WM831X_GPIO5_CONTROL:
+       case WM831X_GPIO6_CONTROL:
+       case WM831X_GPIO7_CONTROL:
+       case WM831X_GPIO8_CONTROL:
+       case WM831X_GPIO9_CONTROL:
+       case WM831X_GPIO10_CONTROL:
+       case WM831X_GPIO11_CONTROL:
+       case WM831X_GPIO12_CONTROL:
+       case WM831X_GPIO13_CONTROL:
+       case WM831X_GPIO14_CONTROL:
+       case WM831X_GPIO15_CONTROL:
+       case WM831X_GPIO16_CONTROL:
+       case WM831X_CHARGER_CONTROL_1:
+       case WM831X_CHARGER_CONTROL_2:
+       case WM831X_CHARGER_STATUS:
+       case WM831X_BACKUP_CHARGER_CONTROL:
+       case WM831X_STATUS_LED_1:
+       case WM831X_STATUS_LED_2:
+       case WM831X_CURRENT_SINK_1:
+       case WM831X_CURRENT_SINK_2:
+       case WM831X_DCDC_ENABLE:
+       case WM831X_LDO_ENABLE:
+       case WM831X_DCDC_STATUS:
+       case WM831X_LDO_STATUS:
+       case WM831X_DCDC_UV_STATUS:
+       case WM831X_LDO_UV_STATUS:
+       case WM831X_DC1_CONTROL_1:
+       case WM831X_DC1_CONTROL_2:
+       case WM831X_DC1_ON_CONFIG:
+       case WM831X_DC1_SLEEP_CONTROL:
+       case WM831X_DC1_DVS_CONTROL:
+       case WM831X_DC2_CONTROL_1:
+       case WM831X_DC2_CONTROL_2:
+       case WM831X_DC2_ON_CONFIG:
+       case WM831X_DC2_SLEEP_CONTROL:
+       case WM831X_DC2_DVS_CONTROL:
+       case WM831X_DC3_CONTROL_1:
+       case WM831X_DC3_CONTROL_2:
+       case WM831X_DC3_ON_CONFIG:
+       case WM831X_DC3_SLEEP_CONTROL:
+       case WM831X_DC4_CONTROL:
+       case WM831X_DC4_SLEEP_CONTROL:
+       case WM831X_EPE1_CONTROL:
+       case WM831X_EPE2_CONTROL:
+       case WM831X_LDO1_CONTROL:
+       case WM831X_LDO1_ON_CONTROL:
+       case WM831X_LDO1_SLEEP_CONTROL:
+       case WM831X_LDO2_CONTROL:
+       case WM831X_LDO2_ON_CONTROL:
+       case WM831X_LDO2_SLEEP_CONTROL:
+       case WM831X_LDO3_CONTROL:
+       case WM831X_LDO3_ON_CONTROL:
+       case WM831X_LDO3_SLEEP_CONTROL:
+       case WM831X_LDO4_CONTROL:
+       case WM831X_LDO4_ON_CONTROL:
+       case WM831X_LDO4_SLEEP_CONTROL:
+       case WM831X_LDO5_CONTROL:
+       case WM831X_LDO5_ON_CONTROL:
+       case WM831X_LDO5_SLEEP_CONTROL:
+       case WM831X_LDO6_CONTROL:
+       case WM831X_LDO6_ON_CONTROL:
+       case WM831X_LDO6_SLEEP_CONTROL:
+       case WM831X_LDO7_CONTROL:
+       case WM831X_LDO7_ON_CONTROL:
+       case WM831X_LDO7_SLEEP_CONTROL:
+       case WM831X_LDO8_CONTROL:
+       case WM831X_LDO8_ON_CONTROL:
+       case WM831X_LDO8_SLEEP_CONTROL:
+       case WM831X_LDO9_CONTROL:
+       case WM831X_LDO9_ON_CONTROL:
+       case WM831X_LDO9_SLEEP_CONTROL:
+       case WM831X_LDO10_CONTROL:
+       case WM831X_LDO10_ON_CONTROL:
+       case WM831X_LDO10_SLEEP_CONTROL:
+       case WM831X_LDO11_ON_CONTROL:
+       case WM831X_LDO11_SLEEP_CONTROL:
+       case WM831X_POWER_GOOD_SOURCE_1:
+       case WM831X_POWER_GOOD_SOURCE_2:
+       case WM831X_CLOCK_CONTROL_1:
+       case WM831X_CLOCK_CONTROL_2:
+       case WM831X_FLL_CONTROL_1:
+       case WM831X_FLL_CONTROL_2:
+       case WM831X_FLL_CONTROL_3:
+       case WM831X_FLL_CONTROL_4:
+       case WM831X_FLL_CONTROL_5:
+       case WM831X_UNIQUE_ID_1:
+       case WM831X_UNIQUE_ID_2:
+       case WM831X_UNIQUE_ID_3:
+       case WM831X_UNIQUE_ID_4:
+       case WM831X_UNIQUE_ID_5:
+       case WM831X_UNIQUE_ID_6:
+       case WM831X_UNIQUE_ID_7:
+       case WM831X_UNIQUE_ID_8:
+       case WM831X_FACTORY_OTP_ID:
+       case WM831X_FACTORY_OTP_1:
+       case WM831X_FACTORY_OTP_2:
+       case WM831X_FACTORY_OTP_3:
+       case WM831X_FACTORY_OTP_4:
+       case WM831X_FACTORY_OTP_5:
+       case WM831X_CUSTOMER_OTP_ID:
+       case WM831X_DC1_OTP_CONTROL:
+       case WM831X_DC2_OTP_CONTROL:
+       case WM831X_DC3_OTP_CONTROL:
+       case WM831X_LDO1_2_OTP_CONTROL:
+       case WM831X_LDO3_4_OTP_CONTROL:
+       case WM831X_LDO5_6_OTP_CONTROL:
+       case WM831X_LDO7_8_OTP_CONTROL:
+       case WM831X_LDO9_10_OTP_CONTROL:
+       case WM831X_LDO11_EPE_CONTROL:
+       case WM831X_GPIO1_OTP_CONTROL:
+       case WM831X_GPIO2_OTP_CONTROL:
+       case WM831X_GPIO3_OTP_CONTROL:
+       case WM831X_GPIO4_OTP_CONTROL:
+       case WM831X_GPIO5_OTP_CONTROL:
+       case WM831X_GPIO6_OTP_CONTROL:
+       case WM831X_DBE_CHECK_DATA:
+               return true;
+       default:
+               return false;
+       }
+}
 
-       ret = wm831x->read_dev(wm831x, reg, bytes, dest);
-       if (ret < 0)
-               return ret;
+static bool wm831x_reg_writeable(struct device *dev, unsigned int reg)
+{
+       struct wm831x *wm831x = dev_get_drvdata(dev);
 
-       for (i = 0; i < bytes / 2; i++) {
-               buf[i] = be16_to_cpu(buf[i]);
+       if (wm831x_reg_locked(wm831x, reg))
+               return false;
 
-               dev_vdbg(wm831x->dev, "Read %04x from R%d(0x%x)\n",
-                        buf[i], reg + i, reg + i);
+       switch (reg) {
+       case WM831X_SYSVDD_CONTROL:
+       case WM831X_THERMAL_MONITORING:
+       case WM831X_POWER_STATE:
+       case WM831X_WATCHDOG:
+       case WM831X_ON_PIN_CONTROL:
+       case WM831X_RESET_CONTROL:
+       case WM831X_CONTROL_INTERFACE:
+       case WM831X_SECURITY_KEY:
+       case WM831X_SOFTWARE_SCRATCH:
+       case WM831X_OTP_CONTROL:
+       case WM831X_GPIO_LEVEL:
+       case WM831X_INTERRUPT_STATUS_1:
+       case WM831X_INTERRUPT_STATUS_2:
+       case WM831X_INTERRUPT_STATUS_3:
+       case WM831X_INTERRUPT_STATUS_4:
+       case WM831X_INTERRUPT_STATUS_5:
+       case WM831X_IRQ_CONFIG:
+       case WM831X_SYSTEM_INTERRUPTS_MASK:
+       case WM831X_INTERRUPT_STATUS_1_MASK:
+       case WM831X_INTERRUPT_STATUS_2_MASK:
+       case WM831X_INTERRUPT_STATUS_3_MASK:
+       case WM831X_INTERRUPT_STATUS_4_MASK:
+       case WM831X_INTERRUPT_STATUS_5_MASK:
+       case WM831X_RTC_TIME_1:
+       case WM831X_RTC_TIME_2:
+       case WM831X_RTC_ALARM_1:
+       case WM831X_RTC_ALARM_2:
+       case WM831X_RTC_CONTROL:
+       case WM831X_RTC_TRIM:
+       case WM831X_TOUCH_CONTROL_1:
+       case WM831X_TOUCH_CONTROL_2:
+       case WM831X_AUXADC_CONTROL:
+       case WM831X_AUXADC_SOURCE:
+       case WM831X_COMPARATOR_CONTROL:
+       case WM831X_COMPARATOR_1:
+       case WM831X_COMPARATOR_2:
+       case WM831X_COMPARATOR_3:
+       case WM831X_COMPARATOR_4:
+       case WM831X_GPIO1_CONTROL:
+       case WM831X_GPIO2_CONTROL:
+       case WM831X_GPIO3_CONTROL:
+       case WM831X_GPIO4_CONTROL:
+       case WM831X_GPIO5_CONTROL:
+       case WM831X_GPIO6_CONTROL:
+       case WM831X_GPIO7_CONTROL:
+       case WM831X_GPIO8_CONTROL:
+       case WM831X_GPIO9_CONTROL:
+       case WM831X_GPIO10_CONTROL:
+       case WM831X_GPIO11_CONTROL:
+       case WM831X_GPIO12_CONTROL:
+       case WM831X_GPIO13_CONTROL:
+       case WM831X_GPIO14_CONTROL:
+       case WM831X_GPIO15_CONTROL:
+       case WM831X_GPIO16_CONTROL:
+       case WM831X_CHARGER_CONTROL_1:
+       case WM831X_CHARGER_CONTROL_2:
+       case WM831X_CHARGER_STATUS:
+       case WM831X_BACKUP_CHARGER_CONTROL:
+       case WM831X_STATUS_LED_1:
+       case WM831X_STATUS_LED_2:
+       case WM831X_CURRENT_SINK_1:
+       case WM831X_CURRENT_SINK_2:
+       case WM831X_DCDC_ENABLE:
+       case WM831X_LDO_ENABLE:
+       case WM831X_DC1_CONTROL_1:
+       case WM831X_DC1_CONTROL_2:
+       case WM831X_DC1_ON_CONFIG:
+       case WM831X_DC1_SLEEP_CONTROL:
+       case WM831X_DC1_DVS_CONTROL:
+       case WM831X_DC2_CONTROL_1:
+       case WM831X_DC2_CONTROL_2:
+       case WM831X_DC2_ON_CONFIG:
+       case WM831X_DC2_SLEEP_CONTROL:
+       case WM831X_DC2_DVS_CONTROL:
+       case WM831X_DC3_CONTROL_1:
+       case WM831X_DC3_CONTROL_2:
+       case WM831X_DC3_ON_CONFIG:
+       case WM831X_DC3_SLEEP_CONTROL:
+       case WM831X_DC4_CONTROL:
+       case WM831X_DC4_SLEEP_CONTROL:
+       case WM831X_EPE1_CONTROL:
+       case WM831X_EPE2_CONTROL:
+       case WM831X_LDO1_CONTROL:
+       case WM831X_LDO1_ON_CONTROL:
+       case WM831X_LDO1_SLEEP_CONTROL:
+       case WM831X_LDO2_CONTROL:
+       case WM831X_LDO2_ON_CONTROL:
+       case WM831X_LDO2_SLEEP_CONTROL:
+       case WM831X_LDO3_CONTROL:
+       case WM831X_LDO3_ON_CONTROL:
+       case WM831X_LDO3_SLEEP_CONTROL:
+       case WM831X_LDO4_CONTROL:
+       case WM831X_LDO4_ON_CONTROL:
+       case WM831X_LDO4_SLEEP_CONTROL:
+       case WM831X_LDO5_CONTROL:
+       case WM831X_LDO5_ON_CONTROL:
+       case WM831X_LDO5_SLEEP_CONTROL:
+       case WM831X_LDO6_CONTROL:
+       case WM831X_LDO6_ON_CONTROL:
+       case WM831X_LDO6_SLEEP_CONTROL:
+       case WM831X_LDO7_CONTROL:
+       case WM831X_LDO7_ON_CONTROL:
+       case WM831X_LDO7_SLEEP_CONTROL:
+       case WM831X_LDO8_CONTROL:
+       case WM831X_LDO8_ON_CONTROL:
+       case WM831X_LDO8_SLEEP_CONTROL:
+       case WM831X_LDO9_CONTROL:
+       case WM831X_LDO9_ON_CONTROL:
+       case WM831X_LDO9_SLEEP_CONTROL:
+       case WM831X_LDO10_CONTROL:
+       case WM831X_LDO10_ON_CONTROL:
+       case WM831X_LDO10_SLEEP_CONTROL:
+       case WM831X_LDO11_ON_CONTROL:
+       case WM831X_LDO11_SLEEP_CONTROL:
+       case WM831X_POWER_GOOD_SOURCE_1:
+       case WM831X_POWER_GOOD_SOURCE_2:
+       case WM831X_CLOCK_CONTROL_1:
+       case WM831X_CLOCK_CONTROL_2:
+       case WM831X_FLL_CONTROL_1:
+       case WM831X_FLL_CONTROL_2:
+       case WM831X_FLL_CONTROL_3:
+       case WM831X_FLL_CONTROL_4:
+       case WM831X_FLL_CONTROL_5:
+               return true;
+       default:
+               return false;
        }
+}
 
-       return 0;
+static bool wm831x_reg_volatile(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case WM831X_SYSTEM_STATUS:
+       case WM831X_ON_SOURCE:
+       case WM831X_OFF_SOURCE:
+       case WM831X_GPIO_LEVEL:
+       case WM831X_SYSTEM_INTERRUPTS:
+       case WM831X_INTERRUPT_STATUS_1:
+       case WM831X_INTERRUPT_STATUS_2:
+       case WM831X_INTERRUPT_STATUS_3:
+       case WM831X_INTERRUPT_STATUS_4:
+       case WM831X_INTERRUPT_STATUS_5:
+       case WM831X_RTC_TIME_1:
+       case WM831X_RTC_TIME_2:
+       case WM831X_TOUCH_DATA_X:
+       case WM831X_TOUCH_DATA_Y:
+       case WM831X_TOUCH_DATA_Z:
+       case WM831X_AUXADC_DATA:
+       case WM831X_CHARGER_STATUS:
+       case WM831X_DCDC_STATUS:
+       case WM831X_LDO_STATUS:
+       case WM831X_DCDC_UV_STATUS:
+       case WM831X_LDO_UV_STATUS:
+               return true;
+       default:
+               return false;
+       }
 }
 
 /**
@@ -191,14 +516,10 @@ static int wm831x_read(struct wm831x *wm831x, unsigned short reg,
  */
 int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg)
 {
-       unsigned short val;
+       unsigned int val;
        int ret;
 
-       mutex_lock(&wm831x->io_lock);
-
-       ret = wm831x_read(wm831x, reg, 2, &val);
-
-       mutex_unlock(&wm831x->io_lock);
+       ret = regmap_read(wm831x->regmap, reg, &val);
 
        if (ret < 0)
                return ret;
@@ -218,15 +539,7 @@ EXPORT_SYMBOL_GPL(wm831x_reg_read);
 int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
                     int count, u16 *buf)
 {
-       int ret;
-
-       mutex_lock(&wm831x->io_lock);
-
-       ret = wm831x_read(wm831x, reg, count * 2, buf);
-
-       mutex_unlock(&wm831x->io_lock);
-
-       return ret;
+       return regmap_bulk_read(wm831x->regmap, reg, buf, count);
 }
 EXPORT_SYMBOL_GPL(wm831x_bulk_read);
 
@@ -234,7 +547,7 @@ static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
                        int bytes, void *src)
 {
        u16 *buf = src;
-       int i;
+       int i, ret;
 
        BUG_ON(bytes % 2);
        BUG_ON(bytes <= 0);
@@ -245,11 +558,10 @@ static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
 
                dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n",
                         buf[i], reg + i, reg + i);
-
-               buf[i] = cpu_to_be16(buf[i]);
+               ret = regmap_write(wm831x->regmap, reg + i, buf[i]);
        }
 
-       return wm831x->write_dev(wm831x, reg, bytes, src);
+       return 0;
 }
 
 /**
@@ -286,20 +598,14 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
                    unsigned short mask, unsigned short val)
 {
        int ret;
-       u16 r;
 
        mutex_lock(&wm831x->io_lock);
 
-       ret = wm831x_read(wm831x, reg, 2, &r);
-       if (ret < 0)
-               goto out;
-
-       r &= ~mask;
-       r |= val & mask;
-
-       ret = wm831x_write(wm831x, reg, 2, &r);
+       if (!wm831x_reg_locked(wm831x, reg))
+               ret = regmap_update_bits(wm831x->regmap, reg, mask, val);
+       else
+               ret = -EPERM;
 
-out:
        mutex_unlock(&wm831x->io_lock);
 
        return ret;
@@ -1292,6 +1598,19 @@ static struct mfd_cell backlight_devs[] = {
        },
 };
 
+struct regmap_config wm831x_regmap_config = {
+       .reg_bits = 16,
+       .val_bits = 16,
+
+       .cache_type = REGCACHE_RBTREE,
+
+       .max_register = WM831X_DBE_CHECK_DATA,
+       .readable_reg = wm831x_reg_readable,
+       .writeable_reg = wm831x_reg_writeable,
+       .volatile_reg = wm831x_reg_volatile,
+};
+EXPORT_SYMBOL_GPL(wm831x_regmap_config);
+
 /*
  * Instantiate the generic non-control parts of the device.
  */
@@ -1305,11 +1624,12 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        mutex_init(&wm831x->io_lock);
        mutex_init(&wm831x->key_lock);
        dev_set_drvdata(wm831x->dev, wm831x);
+       wm831x->soft_shutdown = pdata->soft_shutdown;
 
        ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
        if (ret < 0) {
                dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret);
-               goto err;
+               goto err_regmap;
        }
        switch (ret) {
        case 0x6204:
@@ -1318,20 +1638,20 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        default:
                dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret);
                ret = -EINVAL;
-               goto err;
+               goto err_regmap;
        }
 
        ret = wm831x_reg_read(wm831x, WM831X_REVISION);
        if (ret < 0) {
                dev_err(wm831x->dev, "Failed to read revision: %d\n", ret);
-               goto err;
+               goto err_regmap;
        }
        rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT;
 
        ret = wm831x_reg_read(wm831x, WM831X_RESET_ID);
        if (ret < 0) {
                dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret);
-               goto err;
+               goto err_regmap;
        }
 
        /* Some engineering samples do not have the ID set, rely on
@@ -1406,7 +1726,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        default:
                dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
                ret = -EINVAL;
-               goto err;
+               goto err_regmap;
        }
 
        /* This will need revisiting in future but is OK for all
@@ -1420,7 +1740,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
        if (ret < 0) {
                dev_err(wm831x->dev, "Failed to read security key: %d\n", ret);
-               goto err;
+               goto err_regmap;
        }
        if (ret != 0) {
                dev_warn(wm831x->dev, "Security key had non-zero value %x\n",
@@ -1433,7 +1753,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
                ret = pdata->pre_init(wm831x);
                if (ret != 0) {
                        dev_err(wm831x->dev, "pre_init() failed: %d\n", ret);
-                       goto err;
+                       goto err_regmap;
                }
        }
 
@@ -1456,7 +1776,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
 
        ret = wm831x_irq_init(wm831x, irq);
        if (ret != 0)
-               goto err;
+               goto err_regmap;
 
        wm831x_auxadc_init(wm831x);
 
@@ -1552,8 +1872,9 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
 
 err_irq:
        wm831x_irq_exit(wm831x);
-err:
+err_regmap:
        mfd_remove_devices(wm831x->dev);
+       regmap_exit(wm831x->regmap);
        kfree(wm831x);
        return ret;
 }
@@ -1565,6 +1886,7 @@ void wm831x_device_exit(struct wm831x *wm831x)
        if (wm831x->irq_base)
                free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
        wm831x_irq_exit(wm831x);
+       regmap_exit(wm831x->regmap);
        kfree(wm831x);
 }
 
@@ -1604,6 +1926,15 @@ int wm831x_device_suspend(struct wm831x *wm831x)
        return 0;
 }
 
+void wm831x_device_shutdown(struct wm831x *wm831x)
+{
+       if (wm831x->soft_shutdown) {
+               dev_info(wm831x->dev, "Initiating shutdown...\n");
+               wm831x_set_bits(wm831x, WM831X_POWER_STATE, WM831X_CHIP_ON, 0);
+       }
+}
+EXPORT_SYMBOL_GPL(wm831x_device_shutdown);
+
 MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Mark Brown");
index a06cbc7..ac8da1d 100644 (file)
 #include <linux/delay.h>
 #include <linux/mfd/core.h>
 #include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
 
 #include <linux/mfd/wm831x/core.h>
 #include <linux/mfd/wm831x/pdata.h>
 
-static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
-                                 int bytes, void *dest)
-{
-       struct i2c_client *i2c = wm831x->control_data;
-       int ret;
-       u16 r = cpu_to_be16(reg);
-
-       ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
-       if (ret < 0)
-               return ret;
-       if (ret != 2)
-               return -EIO;
-
-       ret = i2c_master_recv(i2c, dest, bytes);
-       if (ret < 0)
-               return ret;
-       if (ret != bytes)
-               return -EIO;
-       return 0;
-}
-
-/* Currently we allocate the write buffer on the stack; this is OK for
- * small writes - if we need to do large writes this will need to be
- * revised.
- */
-static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
-                                  int bytes, void *src)
-{
-       struct i2c_client *i2c = wm831x->control_data;
-       struct i2c_msg xfer[2];
-       int ret;
-
-       reg = cpu_to_be16(reg);
-
-       xfer[0].addr = i2c->addr;
-       xfer[0].flags = 0;
-       xfer[0].len = 2;
-       xfer[0].buf = (char *)&reg;
-
-       xfer[1].addr = i2c->addr;
-       xfer[1].flags = I2C_M_NOSTART;
-       xfer[1].len = bytes;
-       xfer[1].buf = (char *)src;
-
-       ret = i2c_transfer(i2c->adapter, xfer, 2);
-       if (ret < 0)
-               return ret;
-       if (ret != 2)
-               return -EIO;
-
-       return 0;
-}
-
 static int wm831x_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
        struct wm831x *wm831x;
+       int ret;
 
        wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
        if (wm831x == NULL)
@@ -86,9 +36,15 @@ static int wm831x_i2c_probe(struct i2c_client *i2c,
 
        i2c_set_clientdata(i2c, wm831x);
        wm831x->dev = &i2c->dev;
-       wm831x->control_data = i2c;
-       wm831x->read_dev = wm831x_i2c_read_device;
-       wm831x->write_dev = wm831x_i2c_write_device;
+
+       wm831x->regmap = regmap_init_i2c(i2c, &wm831x_regmap_config);
+       if (IS_ERR(wm831x->regmap)) {
+               ret = PTR_ERR(wm831x->regmap);
+               dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               kfree(wm831x);
+               return ret;
+       }
 
        return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
 }
@@ -109,6 +65,13 @@ static int wm831x_i2c_suspend(struct device *dev)
        return wm831x_device_suspend(wm831x);
 }
 
+static void wm831x_i2c_shutdown(struct i2c_client *i2c)
+{
+       struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+       wm831x_device_shutdown(wm831x);
+}
+
 static const struct i2c_device_id wm831x_i2c_id[] = {
        { "wm8310", WM8310 },
        { "wm8311", WM8311 },
@@ -133,6 +96,7 @@ static struct i2c_driver wm831x_i2c_driver = {
        },
        .probe = wm831x_i2c_probe,
        .remove = wm831x_i2c_remove,
+       .shutdown = wm831x_i2c_shutdown,
        .id_table = wm831x_i2c_id,
 };
 
index eed8e4f..8d6a9a9 100644 (file)
 #include <linux/module.h>
 #include <linux/pm.h>
 #include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
 
 #include <linux/mfd/wm831x/core.h>
 
-static int wm831x_spi_read_device(struct wm831x *wm831x, unsigned short reg,
-                                 int bytes, void *dest)
-{
-       u16 tx_val;
-       u16 *d = dest;
-       int r, ret;
-
-       /* Go register at a time */
-       for (r = reg; r < reg + (bytes / 2); r++) {
-               tx_val = r | 0x8000;
-
-               ret = spi_write_then_read(wm831x->control_data,
-                                         (u8 *)&tx_val, 2, (u8 *)d, 2);
-               if (ret != 0)
-                       return ret;
-
-               *d = be16_to_cpu(*d);
-
-               d++;
-       }
-
-       return 0;
-}
-
-static int wm831x_spi_write_device(struct wm831x *wm831x, unsigned short reg,
-                                  int bytes, void *src)
-{
-       struct spi_device *spi = wm831x->control_data;
-       u16 *s = src;
-       u16 data[2];
-       int ret, r;
-
-       /* Go register at a time */
-       for (r = reg; r < reg + (bytes / 2); r++) {
-               data[0] = r;
-               data[1] = *s++;
-
-               ret = spi_write(spi, (char *)&data, sizeof(data));
-               if (ret != 0)
-                       return ret;
-       }
-
-       return 0;
-}
-
 static int __devinit wm831x_spi_probe(struct spi_device *spi)
 {
+       const struct spi_device_id *id = spi_get_device_id(spi);
        struct wm831x *wm831x;
        enum wm831x_parent type;
+       int ret;
 
-       /* Currently SPI support for ID tables is unmerged, we're faking it */
-       if (strcmp(spi->modalias, "wm8310") == 0)
-               type = WM8310;
-       else if (strcmp(spi->modalias, "wm8311") == 0)
-               type = WM8311;
-       else if (strcmp(spi->modalias, "wm8312") == 0)
-               type = WM8312;
-       else if (strcmp(spi->modalias, "wm8320") == 0)
-               type = WM8320;
-       else if (strcmp(spi->modalias, "wm8321") == 0)
-               type = WM8321;
-       else if (strcmp(spi->modalias, "wm8325") == 0)
-               type = WM8325;
-       else if (strcmp(spi->modalias, "wm8326") == 0)
-               type = WM8326;
-       else {
-               dev_err(&spi->dev, "Unknown device type\n");
-               return -EINVAL;
-       }
+       type = (enum wm831x_parent)id->driver_data;
 
        wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
        if (wm831x == NULL)
@@ -98,9 +39,15 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi)
 
        dev_set_drvdata(&spi->dev, wm831x);
        wm831x->dev = &spi->dev;
-       wm831x->control_data = spi;
-       wm831x->read_dev = wm831x_spi_read_device;
-       wm831x->write_dev = wm831x_spi_write_device;
+
+       wm831x->regmap = regmap_init_spi(spi, &wm831x_regmap_config);
+       if (IS_ERR(wm831x->regmap)) {
+               ret = PTR_ERR(wm831x->regmap);
+               dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               kfree(wm831x);
+               return ret;
+       }
 
        return wm831x_device_init(wm831x, type, spi->irq);
 }
@@ -121,119 +68,50 @@ static int wm831x_spi_suspend(struct device *dev)
        return wm831x_device_suspend(wm831x);
 }
 
+static void wm831x_spi_shutdown(struct spi_device *spi)
+{
+       struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
+
+       wm831x_device_shutdown(wm831x);
+}
+
 static const struct dev_pm_ops wm831x_spi_pm = {
        .freeze = wm831x_spi_suspend,
        .suspend = wm831x_spi_suspend,
 };
 
-static struct spi_driver wm8310_spi_driver = {
-       .driver = {
-               .name   = "wm8310",
-               .bus    = &spi_bus_type,
-               .owner  = THIS_MODULE,
-               .pm     = &wm831x_spi_pm,
-       },
-       .probe          = wm831x_spi_probe,
-       .remove         = __devexit_p(wm831x_spi_remove),
+static const struct spi_device_id wm831x_spi_ids[] = {
+       { "wm8310", WM8310 },
+       { "wm8311", WM8311 },
+       { "wm8312", WM8312 },
+       { "wm8320", WM8320 },
+       { "wm8321", WM8321 },
+       { "wm8325", WM8325 },
+       { "wm8326", WM8326 },
+       { },
 };
+MODULE_DEVICE_TABLE(spi, wm831x_spi_id);
 
-static struct spi_driver wm8311_spi_driver = {
+static struct spi_driver wm831x_spi_driver = {
        .driver = {
-               .name   = "wm8311",
-               .bus    = &spi_bus_type,
-               .owner  = THIS_MODULE,
-               .pm     = &wm831x_spi_pm,
-       },
-       .probe          = wm831x_spi_probe,
-       .remove         = __devexit_p(wm831x_spi_remove),
-};
-
-static struct spi_driver wm8312_spi_driver = {
-       .driver = {
-               .name   = "wm8312",
-               .bus    = &spi_bus_type,
-               .owner  = THIS_MODULE,
-               .pm     = &wm831x_spi_pm,
-       },
-       .probe          = wm831x_spi_probe,
-       .remove         = __devexit_p(wm831x_spi_remove),
-};
-
-static struct spi_driver wm8320_spi_driver = {
-       .driver = {
-               .name   = "wm8320",
-               .bus    = &spi_bus_type,
-               .owner  = THIS_MODULE,
-               .pm     = &wm831x_spi_pm,
-       },
-       .probe          = wm831x_spi_probe,
-       .remove         = __devexit_p(wm831x_spi_remove),
-};
-
-static struct spi_driver wm8321_spi_driver = {
-       .driver = {
-               .name   = "wm8321",
-               .bus    = &spi_bus_type,
-               .owner  = THIS_MODULE,
-               .pm     = &wm831x_spi_pm,
-       },
-       .probe          = wm831x_spi_probe,
-       .remove         = __devexit_p(wm831x_spi_remove),
-};
-
-static struct spi_driver wm8325_spi_driver = {
-       .driver = {
-               .name   = "wm8325",
-               .bus    = &spi_bus_type,
-               .owner  = THIS_MODULE,
-               .pm     = &wm831x_spi_pm,
-       },
-       .probe          = wm831x_spi_probe,
-       .remove         = __devexit_p(wm831x_spi_remove),
-};
-
-static struct spi_driver wm8326_spi_driver = {
-       .driver = {
-               .name   = "wm8326",
+               .name   = "wm831x",
                .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
                .pm     = &wm831x_spi_pm,
        },
+       .id_table       = wm831x_spi_ids,
        .probe          = wm831x_spi_probe,
        .remove         = __devexit_p(wm831x_spi_remove),
+       .shutdown       = wm831x_spi_shutdown,
 };
 
 static int __init wm831x_spi_init(void)
 {
        int ret;
 
-       ret = spi_register_driver(&wm8310_spi_driver);
-       if (ret != 0)
-               pr_err("Failed to register WM8310 SPI driver: %d\n", ret);
-
-       ret = spi_register_driver(&wm8311_spi_driver);
-       if (ret != 0)
-               pr_err("Failed to register WM8311 SPI driver: %d\n", ret);
-
-       ret = spi_register_driver(&wm8312_spi_driver);
-       if (ret != 0)
-               pr_err("Failed to register WM8312 SPI driver: %d\n", ret);
-
-       ret = spi_register_driver(&wm8320_spi_driver);
-       if (ret != 0)
-               pr_err("Failed to register WM8320 SPI driver: %d\n", ret);
-
-       ret = spi_register_driver(&wm8321_spi_driver);
-       if (ret != 0)
-               pr_err("Failed to register WM8321 SPI driver: %d\n", ret);
-
-       ret = spi_register_driver(&wm8325_spi_driver);
-       if (ret != 0)
-               pr_err("Failed to register WM8325 SPI driver: %d\n", ret);
-
-       ret = spi_register_driver(&wm8326_spi_driver);
+       ret = spi_register_driver(&wm831x_spi_driver);
        if (ret != 0)
-               pr_err("Failed to register WM8326 SPI driver: %d\n", ret);
+               pr_err("Failed to register WM831x SPI driver: %d\n", ret);
 
        return 0;
 }
@@ -241,13 +119,7 @@ subsys_initcall(wm831x_spi_init);
 
 static void __exit wm831x_spi_exit(void)
 {
-       spi_unregister_driver(&wm8326_spi_driver);
-       spi_unregister_driver(&wm8325_spi_driver);
-       spi_unregister_driver(&wm8321_spi_driver);
-       spi_unregister_driver(&wm8320_spi_driver);
-       spi_unregister_driver(&wm8312_spi_driver);
-       spi_unregister_driver(&wm8311_spi_driver);
-       spi_unregister_driver(&wm8310_spi_driver);
+       spi_unregister_driver(&wm831x_spi_driver);
 }
 module_exit(wm831x_spi_exit);
 
index 597f82e..e06ba94 100644 (file)
  */
 
 #include <linux/bug.h>
+#include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/kernel.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/wm8400-private.h>
 #include <linux/mfd/wm8400-audio.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 
 static struct {
@@ -123,14 +125,9 @@ static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest)
        /* If there are any volatile reads then read back the entire block */
        for (i = reg; i < reg + num_regs; i++)
                if (reg_data[i].vol) {
-                       ret = wm8400->read_dev(wm8400->io_data, reg,
-                                              num_regs, dest);
-                       if (ret != 0)
-                               return ret;
-                       for (i = 0; i < num_regs; i++)
-                               dest[i] = be16_to_cpu(dest[i]);
-
-                       return 0;
+                       ret = regmap_bulk_read(wm8400->regmap, reg, dest,
+                                              num_regs);
+                       return ret;
                }
 
        /* Otherwise use the cache */
@@ -149,14 +146,11 @@ static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
        for (i = 0; i < num_regs; i++) {
                BUG_ON(!reg_data[reg + i].writable);
                wm8400->reg_cache[reg + i] = src[i];
-               src[i] = cpu_to_be16(src[i]);
+               ret = regmap_write(wm8400->regmap, reg, src[i]);
+               if (ret != 0)
+                       return ret;
        }
 
-       /* Do the actual I/O */
-       ret = wm8400->write_dev(wm8400->io_data, reg, num_regs, src);
-       if (ret != 0)
-               return -EIO;
-
        return 0;
 }
 
@@ -270,14 +264,14 @@ static int wm8400_init(struct wm8400 *wm8400,
        dev_set_drvdata(wm8400->dev, wm8400);
 
        /* Check that this is actually a WM8400 */
-       ret = wm8400->read_dev(wm8400->io_data, WM8400_RESET_ID, 1, &reg);
+       ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, &i);
        if (ret != 0) {
                dev_err(wm8400->dev, "Chip ID register read failed\n");
                return -EIO;
        }
-       if (be16_to_cpu(reg) != reg_data[WM8400_RESET_ID].default_val) {
+       if (i != reg_data[WM8400_RESET_ID].default_val) {
                dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n",
-                       be16_to_cpu(reg));
+                       reg);
                return -ENODEV;
        }
 
@@ -285,9 +279,8 @@ static int wm8400_init(struct wm8400 *wm8400,
         * is a PMIC we can't reset it safely so initialise the register
         * cache from the hardware.
         */
-       ret = wm8400->read_dev(wm8400->io_data, 0,
-                              ARRAY_SIZE(wm8400->reg_cache),
-                              wm8400->reg_cache);
+       ret = regmap_raw_read(wm8400->regmap, 0, wm8400->reg_cache,
+                             ARRAY_SIZE(wm8400->reg_cache));
        if (ret != 0) {
                dev_err(wm8400->dev, "Register cache read failed\n");
                return -EIO;
@@ -337,60 +330,13 @@ static void wm8400_release(struct wm8400 *wm8400)
        mfd_remove_devices(wm8400->dev);
 }
 
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-static int wm8400_i2c_read(void *io_data, char reg, int count, u16 *dest)
-{
-       struct i2c_client *i2c = io_data;
-       struct i2c_msg xfer[2];
-       int ret;
-
-       /* Write register */
-       xfer[0].addr = i2c->addr;
-       xfer[0].flags = 0;
-       xfer[0].len = 1;
-       xfer[0].buf = &reg;
-
-       /* Read data */
-       xfer[1].addr = i2c->addr;
-       xfer[1].flags = I2C_M_RD;
-       xfer[1].len = count * sizeof(u16);
-       xfer[1].buf = (u8 *)dest;
-
-       ret = i2c_transfer(i2c->adapter, xfer, 2);
-       if (ret == 2)
-               ret = 0;
-       else if (ret >= 0)
-               ret = -EIO;
-
-       return ret;
-}
-
-static int wm8400_i2c_write(void *io_data, char reg, int count, const u16 *src)
-{
-       struct i2c_client *i2c = io_data;
-       u8 *msg;
-       int ret;
-
-       /* We add 1 byte for device register - ideally I2C would gather. */
-       msg = kmalloc((count * sizeof(u16)) + 1, GFP_KERNEL);
-       if (msg == NULL)
-               return -ENOMEM;
-
-       msg[0] = reg;
-       memcpy(&msg[1], src, count * sizeof(u16));
-
-       ret = i2c_master_send(i2c, msg, (count * sizeof(u16)) + 1);
-
-       if (ret == (count * 2) + 1)
-               ret = 0;
-       else if (ret >= 0)
-               ret = -EIO;
-
-       kfree(msg);
-
-       return ret;
-}
+static const struct regmap_config wm8400_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 16,
+       .max_register = WM8400_REGISTER_COUNT - 1,
+};
 
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 static int wm8400_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
@@ -403,18 +349,23 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,
                goto err;
        }
 
-       wm8400->io_data = i2c;
-       wm8400->read_dev = wm8400_i2c_read;
-       wm8400->write_dev = wm8400_i2c_write;
+       wm8400->regmap = regmap_init_i2c(i2c, &wm8400_regmap_config);
+       if (IS_ERR(wm8400->regmap)) {
+               ret = PTR_ERR(wm8400->regmap);
+               goto struct_err;
+       }
+
        wm8400->dev = &i2c->dev;
        i2c_set_clientdata(i2c, wm8400);
 
        ret = wm8400_init(wm8400, i2c->dev.platform_data);
        if (ret != 0)
-               goto struct_err;
+               goto map_err;
 
        return 0;
 
+map_err:
+       regmap_exit(wm8400->regmap);
 struct_err:
        kfree(wm8400);
 err:
@@ -426,6 +377,7 @@ static int wm8400_i2c_remove(struct i2c_client *i2c)
        struct wm8400 *wm8400 = i2c_get_clientdata(i2c);
 
        wm8400_release(wm8400);
+       regmap_exit(wm8400->regmap);
        kfree(wm8400);
 
        return 0;
index 96479c9..bfde4e8 100644 (file)
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
+#include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/mfd/core.h>
 #include <linux/pm_runtime.h>
+#include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/regulator/machine.h>
 
 static int wm8994_read(struct wm8994 *wm8994, unsigned short reg,
                       int bytes, void *dest)
 {
-       int ret, i;
-       u16 *buf = dest;
-
-       BUG_ON(bytes % 2);
-       BUG_ON(bytes <= 0);
-
-       ret = wm8994->read_dev(wm8994, reg, bytes, dest);
-       if (ret < 0)
-               return ret;
-
-       for (i = 0; i < bytes / 2; i++) {
-               dev_vdbg(wm8994->dev, "Read %04x from R%d(0x%x)\n",
-                        be16_to_cpu(buf[i]), reg + i, reg + i);
-       }
-
-       return 0;
+       return regmap_raw_read(wm8994->regmap, reg, dest, bytes);
 }
 
 /**
@@ -55,19 +42,15 @@ static int wm8994_read(struct wm8994 *wm8994, unsigned short reg,
  */
 int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg)
 {
-       unsigned short val;
+       unsigned int val;
        int ret;
 
-       mutex_lock(&wm8994->io_lock);
-
-       ret = wm8994_read(wm8994, reg, 2, &val);
-
-       mutex_unlock(&wm8994->io_lock);
+       ret = regmap_read(wm8994->regmap, reg, &val);
 
        if (ret < 0)
                return ret;
        else
-               return be16_to_cpu(val);
+               return val;
 }
 EXPORT_SYMBOL_GPL(wm8994_reg_read);
 
@@ -82,33 +65,13 @@ EXPORT_SYMBOL_GPL(wm8994_reg_read);
 int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
                     int count, u16 *buf)
 {
-       int ret;
-
-       mutex_lock(&wm8994->io_lock);
-
-       ret = wm8994_read(wm8994, reg, count * 2, buf);
-
-       mutex_unlock(&wm8994->io_lock);
-
-       return ret;
+       return regmap_bulk_read(wm8994->regmap, reg, buf, count);
 }
-EXPORT_SYMBOL_GPL(wm8994_bulk_read);
 
 static int wm8994_write(struct wm8994 *wm8994, unsigned short reg,
                        int bytes, const void *src)
 {
-       const u16 *buf = src;
-       int i;
-
-       BUG_ON(bytes % 2);
-       BUG_ON(bytes <= 0);
-
-       for (i = 0; i < bytes / 2; i++) {
-               dev_vdbg(wm8994->dev, "Write %04x to R%d(0x%x)\n",
-                        be16_to_cpu(buf[i]), reg + i, reg + i);
-       }
-
-       return wm8994->write_dev(wm8994, reg, bytes, src);
+       return regmap_raw_write(wm8994->regmap, reg, src, bytes);
 }
 
 /**
@@ -121,17 +84,7 @@ static int wm8994_write(struct wm8994 *wm8994, unsigned short reg,
 int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
                     unsigned short val)
 {
-       int ret;
-
-       val = cpu_to_be16(val);
-
-       mutex_lock(&wm8994->io_lock);
-
-       ret = wm8994_write(wm8994, reg, 2, &val);
-
-       mutex_unlock(&wm8994->io_lock);
-
-       return ret;
+       return regmap_write(wm8994->regmap, reg, val);
 }
 EXPORT_SYMBOL_GPL(wm8994_reg_write);
 
@@ -146,15 +99,7 @@ EXPORT_SYMBOL_GPL(wm8994_reg_write);
 int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg,
                      int count, const u16 *buf)
 {
-       int ret;
-
-       mutex_lock(&wm8994->io_lock);
-
-       ret = wm8994_write(wm8994, reg, count * 2, buf);
-
-       mutex_unlock(&wm8994->io_lock);
-
-       return ret;
+       return regmap_raw_write(wm8994->regmap, reg, buf, count * sizeof(u16));
 }
 EXPORT_SYMBOL_GPL(wm8994_bulk_write);
 
@@ -169,28 +114,7 @@ EXPORT_SYMBOL_GPL(wm8994_bulk_write);
 int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
                    unsigned short mask, unsigned short val)
 {
-       int ret;
-       u16 r;
-
-       mutex_lock(&wm8994->io_lock);
-
-       ret = wm8994_read(wm8994, reg, 2, &r);
-       if (ret < 0)
-               goto out;
-
-       r = be16_to_cpu(r);
-
-       r &= ~mask;
-       r |= val;
-
-       r = cpu_to_be16(r);
-
-       ret = wm8994_write(wm8994, reg, 2, &r);
-
-out:
-       mutex_unlock(&wm8994->io_lock);
-
-       return ret;
+       return regmap_update_bits(wm8994->regmap, reg, mask, val);
 }
 EXPORT_SYMBOL_GPL(wm8994_set_bits);
 
@@ -378,6 +302,11 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
 }
 #endif
 
+static struct regmap_config wm8994_regmap_config = {
+       .reg_bits = 16,
+       .val_bits = 16,
+};
+
 /*
  * Instantiate the generic non-control parts of the device.
  */
@@ -387,7 +316,6 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
        const char *devname;
        int ret, i;
 
-       mutex_init(&wm8994->io_lock);
        dev_set_drvdata(wm8994->dev, wm8994);
 
        /* Add the on-chip regulators first for bootstrapping */
@@ -397,7 +325,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
                              NULL, 0);
        if (ret != 0) {
                dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
-               goto err;
+               goto err_regmap;
        }
 
        switch (wm8994->type) {
@@ -409,7 +337,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
                break;
        default:
                BUG();
-               goto err;
+               goto err_regmap;
        }
 
        wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
@@ -417,7 +345,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
                                   GFP_KERNEL);
        if (!wm8994->supplies) {
                ret = -ENOMEM;
-               goto err;
+               goto err_regmap;
        }
 
        switch (wm8994->type) {
@@ -431,7 +359,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
                break;
        default:
                BUG();
-               goto err;
+               goto err_regmap;
        }
                
        ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
@@ -554,7 +482,8 @@ err_get:
        regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
 err_supplies:
        kfree(wm8994->supplies);
-err:
+err_regmap:
+       regmap_exit(wm8994->regmap);
        mfd_remove_devices(wm8994->dev);
        kfree(wm8994);
        return ret;
@@ -569,62 +498,15 @@ static void wm8994_device_exit(struct wm8994 *wm8994)
                               wm8994->supplies);
        regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
        kfree(wm8994->supplies);
+       regmap_exit(wm8994->regmap);
        kfree(wm8994);
 }
 
-static int wm8994_i2c_read_device(struct wm8994 *wm8994, unsigned short reg,
-                                 int bytes, void *dest)
-{
-       struct i2c_client *i2c = wm8994->control_data;
-       int ret;
-       u16 r = cpu_to_be16(reg);
-
-       ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
-       if (ret < 0)
-               return ret;
-       if (ret != 2)
-               return -EIO;
-
-       ret = i2c_master_recv(i2c, dest, bytes);
-       if (ret < 0)
-               return ret;
-       if (ret != bytes)
-               return -EIO;
-       return 0;
-}
-
-static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg,
-                                  int bytes, const void *src)
-{
-       struct i2c_client *i2c = wm8994->control_data;
-       struct i2c_msg xfer[2];
-       int ret;
-
-       reg = cpu_to_be16(reg);
-
-       xfer[0].addr = i2c->addr;
-       xfer[0].flags = 0;
-       xfer[0].len = 2;
-       xfer[0].buf = (char *)&reg;
-
-       xfer[1].addr = i2c->addr;
-       xfer[1].flags = I2C_M_NOSTART;
-       xfer[1].len = bytes;
-       xfer[1].buf = (char *)src;
-
-       ret = i2c_transfer(i2c->adapter, xfer, 2);
-       if (ret < 0)
-               return ret;
-       if (ret != 2)
-               return -EIO;
-
-       return 0;
-}
-
 static int wm8994_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
        struct wm8994 *wm8994;
+       int ret;
 
        wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL);
        if (wm8994 == NULL)
@@ -632,12 +514,18 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
 
        i2c_set_clientdata(i2c, wm8994);
        wm8994->dev = &i2c->dev;
-       wm8994->control_data = i2c;
-       wm8994->read_dev = wm8994_i2c_read_device;
-       wm8994->write_dev = wm8994_i2c_write_device;
        wm8994->irq = i2c->irq;
        wm8994->type = id->driver_data;
 
+       wm8994->regmap = regmap_init_i2c(i2c, &wm8994_regmap_config);
+       if (IS_ERR(wm8994->regmap)) {
+               ret = PTR_ERR(wm8994->regmap);
+               dev_err(wm8994->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               kfree(wm8994);
+               return ret;
+       }
+
        return wm8994_device_init(wm8994, i2c->irq);
 }
 
index 8dda8de..ed8fe0d 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/completion.h>
 #include <linux/interrupt.h>
 #include <linux/list.h>
+#include <linux/regmap.h>
 
 /*
  * Register values.
@@ -361,12 +362,8 @@ struct wm831x {
        struct mutex io_lock;
 
        struct device *dev;
-       int (*read_dev)(struct wm831x *wm831x, unsigned short reg,
-                       int bytes, void *dest);
-       int (*write_dev)(struct wm831x *wm831x, unsigned short reg,
-                        int bytes, void *src);
 
-       void *control_data;
+       struct regmap *regmap;
 
        int irq;  /* Our chip IRQ */
        struct mutex irq_lock;
@@ -374,6 +371,8 @@ struct wm831x {
        int irq_masks_cur[WM831X_NUM_IRQ_REGS];   /* Currently active value */
        int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
 
+       bool soft_shutdown;
+
        /* Chip revision based flags */
        unsigned has_gpio_ena:1;         /* Has GPIO enable bit */
        unsigned has_cs_sts:1;           /* Has current sink status bit */
@@ -412,8 +411,11 @@ int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
 int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq);
 void wm831x_device_exit(struct wm831x *wm831x);
 int wm831x_device_suspend(struct wm831x *wm831x);
+void wm831x_device_shutdown(struct wm831x *wm831x);
 int wm831x_irq_init(struct wm831x *wm831x, int irq);
 void wm831x_irq_exit(struct wm831x *wm831x);
 void wm831x_auxadc_init(struct wm831x *wm831x);
 
+extern struct regmap_config wm831x_regmap_config;
+
 #endif
index 0ba2459..1d7a3f7 100644 (file)
@@ -123,6 +123,9 @@ struct wm831x_pdata {
        /** Disable the touchscreen */
        bool disable_touch;
 
+       /** The driver should initiate a power off sequence during shutdown */
+       bool soft_shutdown;
+
        int irq_base;
        int gpio_base;
        int gpio_defaults[WM831X_GPIO_NUM];
index 2aab4e9..0147b69 100644 (file)
 #include <linux/mutex.h>
 #include <linux/platform_device.h>
 
+struct regmap;
+
 #define WM8400_REGISTER_COUNT 0x55
 
 struct wm8400 {
        struct device *dev;
 
-       int (*read_dev)(void *data, char reg, int count, u16 *dst);
-       int (*write_dev)(void *data, char reg, int count, const u16 *src);
-
        struct mutex io_lock;
-       void *io_data;
+       struct regmap *regmap;
 
        u16 reg_cache[WM8400_REGISTER_COUNT];
 
index f0b69cd..45df450 100644 (file)
@@ -24,6 +24,7 @@ enum wm8994_type {
 
 struct regulator_dev;
 struct regulator_bulk_data;
+struct regmap;
 
 #define WM8994_NUM_GPIO_REGS 11
 #define WM8994_NUM_LDO_REGS   2
@@ -50,18 +51,12 @@ struct regulator_bulk_data;
 #define WM8994_IRQ_GPIO(x) (x + WM8994_IRQ_TEMP_WARN)
 
 struct wm8994 {
-       struct mutex io_lock;
        struct mutex irq_lock;
 
        enum wm8994_type type;
 
        struct device *dev;
-       int (*read_dev)(struct wm8994 *wm8994, unsigned short reg,
-                       int bytes, void *dest);
-       int (*write_dev)(struct wm8994 *wm8994, unsigned short reg,
-                        int bytes, const void *src);
-
-       void *control_data;
+       struct regmap *regmap;
 
        int gpio_base;
        int irq_base;
index 60a65cd..3daac2d 100644 (file)
 struct i2c_client;
 struct spi_device;
 
+/* An enum of all the supported cache types */
+enum regcache_type {
+       REGCACHE_NONE,
+       REGCACHE_INDEXED,
+       REGCACHE_RBTREE,
+       REGCACHE_LZO
+};
+
+/**
+ * Default value for a register.  We use an array of structs rather
+ * than a simple array as many modern devices have very sparse
+ * register maps.
+ *
+ * @reg: Register address.
+ * @def: Register default value.
+ */
+struct reg_default {
+       unsigned int reg;
+       unsigned int def;
+};
+
+/**
+ * Configuration for the register map of a device.
+ *
+ * @reg_bits: Number of bits in a register address, mandatory.
+ * @val_bits: Number of bits in a register value, mandatory.
+ *
+ * @writeable_reg: Optional callback returning true if the register
+ *                 can be written to.
+ * @readable_reg: Optional callback returning true if the register
+ *                can be read from.
+ * @volatile_reg: Optional callback returning true if the register
+ *                value can't be cached.
+ * @precious_reg: Optional callback returning true if the rgister
+ *                should not be read outside of a call from the driver
+ *                (eg, a clear on read interrupt status register).
+ *
+ * @max_register: Optional, specifies the maximum valid register index.
+ * @reg_defaults: Power on reset values for registers (for use with
+ *                register cache support).
+ * @num_reg_defaults: Number of elements in reg_defaults.
+ *
+ * @read_flag_mask: Mask to be set in the top byte of the register when doing
+ *                  a read.
+ * @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.
+ *
+ * @cache_type: The actual cache type.
+ * @reg_defaults_raw: Power on reset values for registers (for use with
+ *                    register cache support).
+ * @num_reg_defaults_raw: Number of elements in reg_defaults_raw.
+ */
 struct regmap_config {
        int reg_bits;
        int val_bits;
+
+       bool (*writeable_reg)(struct device *dev, unsigned int reg);
+       bool (*readable_reg)(struct device *dev, unsigned int reg);
+       bool (*volatile_reg)(struct device *dev, unsigned int reg);
+       bool (*precious_reg)(struct device *dev, unsigned int reg);
+
+       unsigned int max_register;
+       struct reg_default *reg_defaults;
+       unsigned int num_reg_defaults;
+       enum regcache_type cache_type;
+       const void *reg_defaults_raw;
+       unsigned int num_reg_defaults_raw;
+
+       u8 read_flag_mask;
+       u8 write_flag_mask;
 };
 
 typedef int (*regmap_hw_write)(struct device *dev, const void *data,
@@ -37,25 +105,18 @@ typedef int (*regmap_hw_read)(struct device *dev,
 /**
  * Description of a hardware bus for the register map infrastructure.
  *
- * @list: Internal use.
- * @type: Bus type, used to identify bus to be used for a device.
  * @write: Write operation.
  * @gather_write: Write operation with split register/value, return -ENOTSUPP
  *                if not implemented  on a given device.
  * @read: Read operation.  Data is returned in the buffer used to transmit
  *         data.
- * @owner: Module with the bus implementation, used to pin the implementation
- *         in memory.
  * @read_flag_mask: Mask to be set in the top byte of the register when doing
  *                  a read.
  */
 struct regmap_bus {
-       struct list_head list;
-       struct bus_type *type;
        regmap_hw_write write;
        regmap_hw_gather_write gather_write;
        regmap_hw_read read;
-       struct module *owner;
        u8 read_flag_mask;
 };
 
@@ -79,4 +140,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
 int regmap_update_bits(struct regmap *map, unsigned int reg,
                       unsigned int mask, unsigned int val);
 
+int regcache_sync(struct regmap *map);
+void regcache_cache_only(struct regmap *map, bool enable);
+void regcache_cache_bypass(struct regmap *map, bool enable);
+
 #endif
diff --git a/include/trace/events/regmap.h b/include/trace/events/regmap.h
new file mode 100644 (file)
index 0000000..1e3193b
--- /dev/null
@@ -0,0 +1,136 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM regmap
+
+#if !defined(_TRACE_REGMAP_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_REGMAP_H
+
+#include <linux/device.h>
+#include <linux/ktime.h>
+#include <linux/tracepoint.h>
+
+struct regmap;
+
+/*
+ * Log register events
+ */
+DECLARE_EVENT_CLASS(regmap_reg,
+
+       TP_PROTO(struct device *dev, unsigned int reg,
+                unsigned int val),
+
+       TP_ARGS(dev, reg, val),
+
+       TP_STRUCT__entry(
+               __string(       name,           dev_name(dev)   )
+               __field(        unsigned int,   reg             )
+               __field(        unsigned int,   val             )
+       ),
+
+       TP_fast_assign(
+               __assign_str(name, dev_name(dev));
+               __entry->reg = reg;
+               __entry->val = val;
+       ),
+
+       TP_printk("%s reg=%x val=%x", __get_str(name),
+                 (unsigned int)__entry->reg,
+                 (unsigned int)__entry->val)
+);
+
+DEFINE_EVENT(regmap_reg, regmap_reg_write,
+
+       TP_PROTO(struct device *dev, unsigned int reg,
+                unsigned int val),
+
+       TP_ARGS(dev, reg, val)
+
+);
+
+DEFINE_EVENT(regmap_reg, regmap_reg_read,
+
+       TP_PROTO(struct device *dev, unsigned int reg,
+                unsigned int val),
+
+       TP_ARGS(dev, reg, val)
+
+);
+
+DECLARE_EVENT_CLASS(regmap_block,
+
+       TP_PROTO(struct device *dev, unsigned int reg, int count),
+
+       TP_ARGS(dev, reg, count),
+
+       TP_STRUCT__entry(
+               __string(       name,           dev_name(dev)   )
+               __field(        unsigned int,   reg             )
+               __field(        int,            count           )
+       ),
+
+       TP_fast_assign(
+               __assign_str(name, dev_name(dev));
+               __entry->reg = reg;
+               __entry->count = count;
+       ),
+
+       TP_printk("%s reg=%x count=%d", __get_str(name),
+                 (unsigned int)__entry->reg,
+                 (int)__entry->count)
+);
+
+DEFINE_EVENT(regmap_block, regmap_hw_read_start,
+
+       TP_PROTO(struct device *dev, unsigned int reg, int count),
+
+       TP_ARGS(dev, reg, count)
+);
+
+DEFINE_EVENT(regmap_block, regmap_hw_read_done,
+
+       TP_PROTO(struct device *dev, unsigned int reg, int count),
+
+       TP_ARGS(dev, reg, count)
+);
+
+DEFINE_EVENT(regmap_block, regmap_hw_write_start,
+
+       TP_PROTO(struct device *dev, unsigned int reg, int count),
+
+       TP_ARGS(dev, reg, count)
+);
+
+DEFINE_EVENT(regmap_block, regmap_hw_write_done,
+
+       TP_PROTO(struct device *dev, unsigned int reg, int count),
+
+       TP_ARGS(dev, reg, count)
+);
+
+TRACE_EVENT(regcache_sync,
+
+       TP_PROTO(struct device *dev, const char *type,
+                const char *status),
+
+       TP_ARGS(dev, type, status),
+
+       TP_STRUCT__entry(
+               __string(       name,           dev_name(dev)   )
+               __string(       status,         status          )
+               __string(       type,           type            )
+               __field(        int,            type            )
+       ),
+
+       TP_fast_assign(
+               __assign_str(name, dev_name(dev));
+               __assign_str(status, status);
+               __assign_str(type, type);
+       ),
+
+       TP_printk("%s type=%s status=%s", __get_str(name),
+                 __get_str(type), __get_str(status))
+);
+
+#endif /* _TRACE_REGMAP_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>