Merge tag 'regmap-3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 20 Feb 2013 17:20:55 +0000 (09:20 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 20 Feb 2013 17:20:55 +0000 (09:20 -0800)
Pull regmap updates from Mark Brown:
 "Several nice new features and performance improvements here,
  especially the first:

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

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

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

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

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

   - Support for 24 bit register addresses.

   - More performance improvements for debugfs."

* tag 'regmap-3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: (24 commits)
  regmap: mmio: add register clock support
  regmap: debugfs: Factor out debugfs_tot_len calc into a function
  regmap: debugfs: Optimize seeking within blocks of registers
  regmap: debugfs: Add a `max_reg' member in struct regmap_debugfs_off_cache
  regmap: debugfs: Fix reading in register field units
  regmap: spi: Handle allocation failures gracefully
  regmap: Export regmap_async_complete()
  regmap: Export regmap_async_complete_cb
  regmap: include linux/sched.h to fix build
  regmap: spi: Support asynchronous I/O for SPI
  regmap: Add asynchronous I/O support
  regmap: Add "no-bus" option for regmap API
  regmap: regmap: avoid spurious warning in regmap_read_debugfs
  regmap: Add provisions to have user-defined write operation
  regmap: Add provisions to have user-defined read operation
  regmap: Add support for 24 bit wide register addresses
  mfd: wm5110: Mark wakes as inverted
  mfd: wm5102: Mark wakes as inverted
  regmap: irq: Support wake IRQ mask inversion
  regmap: irq: Fix sync of wake statuses to hardware
  ...

12 files changed:
drivers/base/regmap/Makefile
drivers/base/regmap/internal.h
drivers/base/regmap/regcache-flat.c [new file with mode: 0644]
drivers/base/regmap/regcache.c
drivers/base/regmap/regmap-debugfs.c
drivers/base/regmap/regmap-irq.c
drivers/base/regmap/regmap-mmio.c
drivers/base/regmap/regmap-spi.c
drivers/base/regmap/regmap.c
drivers/mfd/wm5102-tables.c
drivers/mfd/wm5110-tables.c
include/linux/regmap.h

index 5e75d1b..cf12998 100644 (file)
@@ -1,5 +1,5 @@
 obj-$(CONFIG_REGMAP) += regmap.o regcache.o
-obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o
+obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
 obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
 obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
 obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
index 401d191..5a22bd3 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/regmap.h>
 #include <linux/fs.h>
 #include <linux/list.h>
+#include <linux/wait.h>
 
 struct regmap;
 struct regcache_ops;
@@ -25,6 +26,7 @@ struct regmap_debugfs_off_cache {
        off_t min;
        off_t max;
        unsigned int base_reg;
+       unsigned int max_reg;
 };
 
 struct regmap_format {
@@ -39,6 +41,13 @@ struct regmap_format {
        unsigned int (*parse_val)(void *buf);
 };
 
+struct regmap_async {
+       struct list_head list;
+       struct work_struct cleanup;
+       struct regmap *map;
+       void *work_buf;
+};
+
 struct regmap {
        struct mutex mutex;
        spinlock_t spinlock;
@@ -53,6 +62,11 @@ struct regmap {
        void *bus_context;
        const char *name;
 
+       spinlock_t async_lock;
+       wait_queue_head_t async_waitq;
+       struct list_head async_list;
+       int async_ret;
+
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs;
        const char *debugfs_name;
@@ -74,6 +88,11 @@ struct regmap {
        const struct regmap_access_table *volatile_table;
        const struct regmap_access_table *precious_table;
 
+       int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
+       int (*reg_write)(void *context, unsigned int reg, unsigned int val);
+
+       bool defer_caching;
+
        u8 read_flag_mask;
        u8 write_flag_mask;
 
@@ -175,7 +194,10 @@ bool regcache_set_val(void *base, unsigned int idx,
                      unsigned int val, unsigned int word_size);
 int regcache_lookup_reg(struct regmap *map, unsigned int reg);
 
+void regmap_async_complete_cb(struct regmap_async *async, int ret);
+
 extern struct regcache_ops regcache_rbtree_ops;
 extern struct regcache_ops regcache_lzo_ops;
+extern struct regcache_ops regcache_flat_ops;
 
 #endif
diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
new file mode 100644 (file)
index 0000000..d9762e4
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Register cache access API - flat caching support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/seq_file.h>
+
+#include "internal.h"
+
+static int regcache_flat_init(struct regmap *map)
+{
+       int i;
+       unsigned int *cache;
+
+       map->cache = kzalloc(sizeof(unsigned int) * (map->max_register + 1),
+                            GFP_KERNEL);
+       if (!map->cache)
+               return -ENOMEM;
+
+       cache = map->cache;
+
+       for (i = 0; i < map->num_reg_defaults; i++)
+               cache[map->reg_defaults[i].reg] = map->reg_defaults[i].def;
+
+       return 0;
+}
+
+static int regcache_flat_exit(struct regmap *map)
+{
+       kfree(map->cache);
+       map->cache = NULL;
+
+       return 0;
+}
+
+static int regcache_flat_read(struct regmap *map,
+                             unsigned int reg, unsigned int *value)
+{
+       unsigned int *cache = map->cache;
+
+       *value = cache[reg];
+
+       return 0;
+}
+
+static int regcache_flat_write(struct regmap *map, unsigned int reg,
+                              unsigned int value)
+{
+       unsigned int *cache = map->cache;
+
+       cache[reg] = value;
+
+       return 0;
+}
+
+struct regcache_ops regcache_flat_ops = {
+       .type = REGCACHE_FLAT,
+       .name = "flat",
+       .init = regcache_flat_init,
+       .exit = regcache_flat_exit,
+       .read = regcache_flat_read,
+       .write = regcache_flat_write,
+};
index 835883b..e69ff3e 100644 (file)
@@ -22,6 +22,7 @@
 static const struct regcache_ops *cache_types[] = {
        &regcache_rbtree_ops,
        &regcache_lzo_ops,
+       &regcache_flat_ops,
 };
 
 static int regcache_hw_init(struct regmap *map)
index d9a6c94..78d5f20 100644 (file)
@@ -81,6 +81,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
        struct regmap_debugfs_off_cache *c = NULL;
        loff_t p = 0;
        unsigned int i, ret;
+       unsigned int fpos_offset;
+       unsigned int reg_offset;
 
        /*
         * If we don't have a cache build one so we don't have to do a
@@ -93,6 +95,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
                            regmap_precious(map, i)) {
                                if (c) {
                                        c->max = p - 1;
+                                       fpos_offset = c->max - c->min;
+                                       reg_offset = fpos_offset / map->debugfs_tot_len;
+                                       c->max_reg = c->base_reg + reg_offset;
                                        list_add_tail(&c->list,
                                                      &map->debugfs_off_cache);
                                        c = NULL;
@@ -119,6 +124,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
        /* Close the last entry off if we didn't scan beyond it */
        if (c) {
                c->max = p - 1;
+               fpos_offset = c->max - c->min;
+               reg_offset = fpos_offset / map->debugfs_tot_len;
+               c->max_reg = c->base_reg + reg_offset;
                list_add_tail(&c->list,
                              &map->debugfs_off_cache);
        }
@@ -128,25 +136,38 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
         * allocate and we should never be in this code if there are
         * no registers at all.
         */
-       if (list_empty(&map->debugfs_off_cache)) {
-               WARN_ON(list_empty(&map->debugfs_off_cache));
-               return base;
-       }
+       WARN_ON(list_empty(&map->debugfs_off_cache));
+       ret = base;
 
-       /* Find the relevant block */
+       /* Find the relevant block:offset */
        list_for_each_entry(c, &map->debugfs_off_cache, list) {
                if (from >= c->min && from <= c->max) {
-                       *pos = c->min;
-                       return c->base_reg;
+                       fpos_offset = from - c->min;
+                       reg_offset = fpos_offset / map->debugfs_tot_len;
+                       *pos = c->min + (reg_offset * map->debugfs_tot_len);
+                       return c->base_reg + reg_offset;
                }
 
-               *pos = c->min;
-               ret = c->base_reg;
+               *pos = c->max;
+               ret = c->max_reg;
        }
 
        return ret;
 }
 
+static inline void regmap_calc_tot_len(struct regmap *map,
+                                      void *buf, size_t count)
+{
+       /* Calculate the length of a fixed format  */
+       if (!map->debugfs_tot_len) {
+               map->debugfs_reg_len = regmap_calc_reg_len(map->max_register,
+                                                          buf, count);
+               map->debugfs_val_len = 2 * map->format.val_bytes;
+               map->debugfs_tot_len = map->debugfs_reg_len +
+                       map->debugfs_val_len + 3;      /* : \n */
+       }
+}
+
 static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
                                   unsigned int to, char __user *user_buf,
                                   size_t count, loff_t *ppos)
@@ -165,14 +186,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
        if (!buf)
                return -ENOMEM;
 
-       /* Calculate the length of a fixed format  */
-       if (!map->debugfs_tot_len) {
-               map->debugfs_reg_len = regmap_calc_reg_len(map->max_register,
-                                                          buf, count);
-               map->debugfs_val_len = 2 * map->format.val_bytes;
-               map->debugfs_tot_len = map->debugfs_reg_len +
-                       map->debugfs_val_len + 3;      /* : \n */
-       }
+       regmap_calc_tot_len(map, buf, count);
 
        /* Work out which register we're starting at */
        start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p);
@@ -187,7 +201,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
                /* If we're in the region the user is trying to read */
                if (p >= *ppos) {
                        /* ...but not beyond it */
-                       if (buf_pos + 1 + map->debugfs_tot_len >= count)
+                       if (buf_pos + map->debugfs_tot_len > count)
                                break;
 
                        /* Format the register */
index 5972ad9..4706c63 100644 (file)
@@ -34,6 +34,7 @@ struct regmap_irq_chip_data {
        int irq;
        int wake_count;
 
+       void *status_reg_buf;
        unsigned int *status_buf;
        unsigned int *mask_buf;
        unsigned int *mask_buf_def;
@@ -87,6 +88,23 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
                if (ret != 0)
                        dev_err(d->map->dev, "Failed to sync masks in %x\n",
                                reg);
+
+               reg = d->chip->wake_base +
+                       (i * map->reg_stride * d->irq_reg_stride);
+               if (d->wake_buf) {
+                       if (d->chip->wake_invert)
+                               ret = regmap_update_bits(d->map, reg,
+                                                        d->mask_buf_def[i],
+                                                        ~d->wake_buf[i]);
+                       else
+                               ret = regmap_update_bits(d->map, reg,
+                                                        d->mask_buf_def[i],
+                                                        d->wake_buf[i]);
+                       if (ret != 0)
+                               dev_err(d->map->dev,
+                                       "Failed to sync wakes in %x: %d\n",
+                                       reg, ret);
+               }
        }
 
        if (d->chip->runtime_pm)
@@ -129,16 +147,15 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
        struct regmap *map = d->map;
        const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
 
-       if (!d->chip->wake_base)
-               return -EINVAL;
-
        if (on) {
-               d->wake_buf[irq_data->reg_offset / map->reg_stride]
-                       &= ~irq_data->mask;
+               if (d->wake_buf)
+                       d->wake_buf[irq_data->reg_offset / map->reg_stride]
+                               &= ~irq_data->mask;
                d->wake_count++;
        } else {
-               d->wake_buf[irq_data->reg_offset / map->reg_stride]
-                       |= irq_data->mask;
+               if (d->wake_buf)
+                       d->wake_buf[irq_data->reg_offset / map->reg_stride]
+                               |= irq_data->mask;
                d->wake_count--;
        }
 
@@ -172,25 +189,69 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
        }
 
        /*
-        * Ignore masked IRQs and ack if we need to; we ack early so
-        * there is no race between handling and acknowleding the
-        * interrupt.  We assume that typically few of the interrupts
-        * will fire simultaneously so don't worry about overhead from
-        * doing a write per register.
+        * Read in the statuses, using a single bulk read if possible
+        * in order to reduce the I/O overheads.
         */
-       for (i = 0; i < data->chip->num_regs; i++) {
-               ret = regmap_read(map, chip->status_base + (i * map->reg_stride
-                                  * data->irq_reg_stride),
-                                  &data->status_buf[i]);
+       if (!map->use_single_rw && map->reg_stride == 1 &&
+           data->irq_reg_stride == 1) {
+               u8 *buf8 = data->status_reg_buf;
+               u16 *buf16 = data->status_reg_buf;
+               u32 *buf32 = data->status_reg_buf;
 
+               BUG_ON(!data->status_reg_buf);
+
+               ret = regmap_bulk_read(map, chip->status_base,
+                                      data->status_reg_buf,
+                                      chip->num_regs);
                if (ret != 0) {
                        dev_err(map->dev, "Failed to read IRQ status: %d\n",
-                                       ret);
-                       if (chip->runtime_pm)
-                               pm_runtime_put(map->dev);
+                               ret);
                        return IRQ_NONE;
                }
 
+               for (i = 0; i < data->chip->num_regs; i++) {
+                       switch (map->format.val_bytes) {
+                       case 1:
+                               data->status_buf[i] = buf8[i];
+                               break;
+                       case 2:
+                               data->status_buf[i] = buf16[i];
+                               break;
+                       case 4:
+                               data->status_buf[i] = buf32[i];
+                               break;
+                       default:
+                               BUG();
+                               return IRQ_NONE;
+                       }
+               }
+
+       } else {
+               for (i = 0; i < data->chip->num_regs; i++) {
+                       ret = regmap_read(map, chip->status_base +
+                                         (i * map->reg_stride
+                                          * data->irq_reg_stride),
+                                         &data->status_buf[i]);
+
+                       if (ret != 0) {
+                               dev_err(map->dev,
+                                       "Failed to read IRQ status: %d\n",
+                                       ret);
+                               if (chip->runtime_pm)
+                                       pm_runtime_put(map->dev);
+                               return IRQ_NONE;
+                       }
+               }
+       }
+
+       /*
+        * Ignore masked IRQs and ack if we need to; we ack early so
+        * there is no race between handling and acknowleding the
+        * interrupt.  We assume that typically few of the interrupts
+        * will fire simultaneously so don't worry about overhead from
+        * doing a write per register.
+        */
+       for (i = 0; i < data->chip->num_regs; i++) {
                data->status_buf[i] &= ~data->mask_buf[i];
 
                if (data->status_buf[i] && chip->ack_base) {
@@ -316,11 +377,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
 
        d->irq_chip = regmap_irq_chip;
        d->irq_chip.name = chip->name;
-       if (!chip->wake_base) {
-               d->irq_chip.irq_set_wake = NULL;
-               d->irq_chip.flags |= IRQCHIP_MASK_ON_SUSPEND |
-                                    IRQCHIP_SKIP_SET_WAKE;
-       }
        d->irq = irq;
        d->map = map;
        d->chip = chip;
@@ -331,6 +387,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
        else
                d->irq_reg_stride = 1;
 
+       if (!map->use_single_rw && map->reg_stride == 1 &&
+           d->irq_reg_stride == 1) {
+               d->status_reg_buf = kmalloc(map->format.val_bytes *
+                                           chip->num_regs, GFP_KERNEL);
+               if (!d->status_reg_buf)
+                       goto err_alloc;
+       }
+
        mutex_init(&d->lock);
 
        for (i = 0; i < chip->num_irqs; i++)
@@ -361,8 +425,15 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
                        d->wake_buf[i] = d->mask_buf_def[i];
                        reg = chip->wake_base +
                                (i * map->reg_stride * d->irq_reg_stride);
-                       ret = regmap_update_bits(map, reg, d->wake_buf[i],
-                                                d->wake_buf[i]);
+
+                       if (chip->wake_invert)
+                               ret = regmap_update_bits(map, reg,
+                                                        d->mask_buf_def[i],
+                                                        0);
+                       else
+                               ret = regmap_update_bits(map, reg,
+                                                        d->mask_buf_def[i],
+                                                        d->wake_buf[i]);
                        if (ret != 0) {
                                dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
                                        reg, ret);
@@ -401,6 +472,7 @@ err_alloc:
        kfree(d->mask_buf_def);
        kfree(d->mask_buf);
        kfree(d->status_buf);
+       kfree(d->status_reg_buf);
        kfree(d);
        return ret;
 }
@@ -422,6 +494,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
        kfree(d->wake_buf);
        kfree(d->mask_buf_def);
        kfree(d->mask_buf);
+       kfree(d->status_reg_buf);
        kfree(d->status_buf);
        kfree(d);
 }
index f05fc74..98745dd 100644 (file)
@@ -16,6 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/io.h>
@@ -26,6 +27,7 @@
 struct regmap_mmio_context {
        void __iomem *regs;
        unsigned val_bytes;
+       struct clk *clk;
 };
 
 static int regmap_mmio_gather_write(void *context,
@@ -34,9 +36,16 @@ static int regmap_mmio_gather_write(void *context,
 {
        struct regmap_mmio_context *ctx = context;
        u32 offset;
+       int ret;
 
        BUG_ON(reg_size != 4);
 
+       if (ctx->clk) {
+               ret = clk_enable(ctx->clk);
+               if (ret < 0)
+                       return ret;
+       }
+
        offset = *(u32 *)reg;
 
        while (val_size) {
@@ -64,6 +73,9 @@ static int regmap_mmio_gather_write(void *context,
                offset += ctx->val_bytes;
        }
 
+       if (ctx->clk)
+               clk_disable(ctx->clk);
+
        return 0;
 }
 
@@ -80,9 +92,16 @@ static int regmap_mmio_read(void *context,
 {
        struct regmap_mmio_context *ctx = context;
        u32 offset;
+       int ret;
 
        BUG_ON(reg_size != 4);
 
+       if (ctx->clk) {
+               ret = clk_enable(ctx->clk);
+               if (ret < 0)
+                       return ret;
+       }
+
        offset = *(u32 *)reg;
 
        while (val_size) {
@@ -110,11 +129,20 @@ static int regmap_mmio_read(void *context,
                offset += ctx->val_bytes;
        }
 
+       if (ctx->clk)
+               clk_disable(ctx->clk);
+
        return 0;
 }
 
 static void regmap_mmio_free_context(void *context)
 {
+       struct regmap_mmio_context *ctx = context;
+
+       if (ctx->clk) {
+               clk_unprepare(ctx->clk);
+               clk_put(ctx->clk);
+       }
        kfree(context);
 }
 
@@ -128,11 +156,14 @@ static struct regmap_bus regmap_mmio = {
        .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
 };
 
-static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
+static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
+                                       const char *clk_id,
+                                       void __iomem *regs,
                                        const struct regmap_config *config)
 {
        struct regmap_mmio_context *ctx;
        int min_stride;
+       int ret;
 
        if (config->reg_bits != 32)
                return ERR_PTR(-EINVAL);
@@ -179,37 +210,59 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
        ctx->regs = regs;
        ctx->val_bytes = config->val_bits / 8;
 
+       if (clk_id == NULL)
+               return ctx;
+
+       ctx->clk = clk_get(dev, clk_id);
+       if (IS_ERR(ctx->clk)) {
+               ret = PTR_ERR(ctx->clk);
+               goto err_free;
+       }
+
+       ret = clk_prepare(ctx->clk);
+       if (ret < 0) {
+               clk_put(ctx->clk);
+               goto err_free;
+       }
+
        return ctx;
+
+err_free:
+       kfree(ctx);
+
+       return ERR_PTR(ret);
 }
 
 /**
- * regmap_init_mmio(): Initialise register map
+ * regmap_init_mmio_clk(): Initialise register map with register clock
  *
  * @dev: Device that will be interacted with
+ * @clk_id: register clock consumer ID
  * @regs: Pointer to memory-mapped IO region
  * @config: Configuration for register map
  *
  * The return value will be an ERR_PTR() on error or a valid pointer to
  * a struct regmap.
  */
-struct regmap *regmap_init_mmio(struct device *dev,
-                               void __iomem *regs,
-                               const struct regmap_config *config)
+struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
+                                   void __iomem *regs,
+                                   const struct regmap_config *config)
 {
        struct regmap_mmio_context *ctx;
 
-       ctx = regmap_mmio_gen_context(regs, config);
+       ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
        if (IS_ERR(ctx))
                return ERR_CAST(ctx);
 
        return regmap_init(dev, &regmap_mmio, ctx, config);
 }
-EXPORT_SYMBOL_GPL(regmap_init_mmio);
+EXPORT_SYMBOL_GPL(regmap_init_mmio_clk);
 
 /**
- * devm_regmap_init_mmio(): Initialise managed register map
+ * devm_regmap_init_mmio_clk(): Initialise managed register map with clock
  *
  * @dev: Device that will be interacted with
+ * @clk_id: register clock consumer ID
  * @regs: Pointer to memory-mapped IO region
  * @config: Configuration for register map
  *
@@ -217,18 +270,18 @@ EXPORT_SYMBOL_GPL(regmap_init_mmio);
  * to a struct regmap.  The regmap will be automatically freed by the
  * device management code.
  */
-struct regmap *devm_regmap_init_mmio(struct device *dev,
-                                    void __iomem *regs,
-                                    const struct regmap_config *config)
+struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
+                                        void __iomem *regs,
+                                        const struct regmap_config *config)
 {
        struct regmap_mmio_context *ctx;
 
-       ctx = regmap_mmio_gen_context(regs, config);
+       ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
        if (IS_ERR(ctx))
                return ERR_CAST(ctx);
 
        return devm_regmap_init(dev, &regmap_mmio, ctx, config);
 }
-EXPORT_SYMBOL_GPL(devm_regmap_init_mmio);
+EXPORT_SYMBOL_GPL(devm_regmap_init_mmio_clk);
 
 MODULE_LICENSE("GPL v2");
index ffa46a9..4c506bd 100644 (file)
 #include <linux/init.h>
 #include <linux/module.h>
 
+#include "internal.h"
+
+struct regmap_async_spi {
+       struct regmap_async core;
+       struct spi_message m;
+       struct spi_transfer t[2];
+};
+
+static void regmap_spi_complete(void *data)
+{
+       struct regmap_async_spi *async = data;
+
+       regmap_async_complete_cb(&async->core, async->m.status);
+}
+
 static int regmap_spi_write(void *context, const void *data, size_t count)
 {
        struct device *dev = context;
@@ -40,6 +55,43 @@ static int regmap_spi_gather_write(void *context,
        return spi_sync(spi, &m);
 }
 
+static int regmap_spi_async_write(void *context,
+                                 const void *reg, size_t reg_len,
+                                 const void *val, size_t val_len,
+                                 struct regmap_async *a)
+{
+       struct regmap_async_spi *async = container_of(a,
+                                                     struct regmap_async_spi,
+                                                     core);
+       struct device *dev = context;
+       struct spi_device *spi = to_spi_device(dev);
+
+       async->t[0].tx_buf = reg;
+       async->t[0].len = reg_len;
+       async->t[1].tx_buf = val;
+       async->t[1].len = val_len;
+
+       spi_message_init(&async->m);
+       spi_message_add_tail(&async->t[0], &async->m);
+       spi_message_add_tail(&async->t[1], &async->m);
+
+       async->m.complete = regmap_spi_complete;
+       async->m.context = async;
+
+       return spi_async(spi, &async->m);
+}
+
+static struct regmap_async *regmap_spi_async_alloc(void)
+{
+       struct regmap_async_spi *async_spi;
+
+       async_spi = kzalloc(sizeof(*async_spi), GFP_KERNEL);
+       if (!async_spi)
+               return NULL;
+
+       return &async_spi->core;
+}
+
 static int regmap_spi_read(void *context,
                           const void *reg, size_t reg_size,
                           void *val, size_t val_size)
@@ -53,6 +105,8 @@ static int regmap_spi_read(void *context,
 static struct regmap_bus regmap_spi = {
        .write = regmap_spi_write,
        .gather_write = regmap_spi_gather_write,
+       .async_write = regmap_spi_async_write,
+       .async_alloc = regmap_spi_async_alloc,
        .read = regmap_spi_read,
        .read_flag_mask = 0x80,
 };
index f00b059..3d23675 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/mutex.h>
 #include <linux/err.h>
 #include <linux/rbtree.h>
+#include <linux/sched.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/regmap.h>
@@ -34,6 +35,22 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
                               unsigned int mask, unsigned int val,
                               bool *change);
 
+static int _regmap_bus_read(void *context, unsigned int reg,
+                           unsigned int *val);
+static int _regmap_bus_formatted_write(void *context, unsigned int reg,
+                                      unsigned int val);
+static int _regmap_bus_raw_write(void *context, unsigned int reg,
+                                unsigned int val);
+
+static void async_cleanup(struct work_struct *work)
+{
+       struct regmap_async *async = container_of(work, struct regmap_async,
+                                                 cleanup);
+
+       kfree(async->work_buf);
+       kfree(async);
+}
+
 bool regmap_reg_in_ranges(unsigned int reg,
                          const struct regmap_range *ranges,
                          unsigned int nranges)
@@ -372,7 +389,7 @@ struct regmap *regmap_init(struct device *dev,
        enum regmap_endian reg_endian, val_endian;
        int i, j;
 
-       if (!bus || !config)
+       if (!config)
                goto err;
 
        map = kzalloc(sizeof(*map), GFP_KERNEL);
@@ -386,7 +403,8 @@ struct regmap *regmap_init(struct device *dev,
                map->unlock = config->unlock;
                map->lock_arg = config->lock_arg;
        } else {
-               if (bus->fast_io) {
+               if ((bus && bus->fast_io) ||
+                   config->fast_io) {
                        spin_lock_init(&map->spinlock);
                        map->lock = regmap_lock_spinlock;
                        map->unlock = regmap_unlock_spinlock;
@@ -423,13 +441,27 @@ struct regmap *regmap_init(struct device *dev,
        map->cache_type = config->cache_type;
        map->name = config->name;
 
+       spin_lock_init(&map->async_lock);
+       INIT_LIST_HEAD(&map->async_list);
+       init_waitqueue_head(&map->async_waitq);
+
        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 {
+       } else if (bus) {
                map->read_flag_mask = bus->read_flag_mask;
        }
 
+       if (!bus) {
+               map->reg_read  = config->reg_read;
+               map->reg_write = config->reg_write;
+
+               map->defer_caching = false;
+               goto skip_format_initialization;
+       } else {
+               map->reg_read  = _regmap_bus_read;
+       }
+
        reg_endian = config->reg_format_endian;
        if (reg_endian == REGMAP_ENDIAN_DEFAULT)
                reg_endian = bus->reg_format_endian_default;
@@ -500,6 +532,12 @@ struct regmap *regmap_init(struct device *dev,
                }
                break;
 
+       case 24:
+               if (reg_endian != REGMAP_ENDIAN_BIG)
+                       goto err_map;
+               map->format.format_reg = regmap_format_24;
+               break;
+
        case 32:
                switch (reg_endian) {
                case REGMAP_ENDIAN_BIG:
@@ -575,6 +613,16 @@ struct regmap *regmap_init(struct device *dev,
                goto err_map;
        }
 
+       if (map->format.format_write) {
+               map->defer_caching = false;
+               map->reg_write = _regmap_bus_formatted_write;
+       } else if (map->format.format_val) {
+               map->defer_caching = true;
+               map->reg_write = _regmap_bus_raw_write;
+       }
+
+skip_format_initialization:
+
        map->range_tree = RB_ROOT;
        for (i = 0; i < config->num_ranges; i++) {
                const struct regmap_range_cfg *range_cfg = &config->ranges[i];
@@ -776,7 +824,7 @@ void regmap_exit(struct regmap *map)
        regcache_exit(map);
        regmap_debugfs_exit(map);
        regmap_range_exit(map);
-       if (map->bus->free_context)
+       if (map->bus && map->bus->free_context)
                map->bus->free_context(map->bus_context);
        kfree(map->work_buf);
        kfree(map);
@@ -870,15 +918,20 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
 }
 
 static int _regmap_raw_write(struct regmap *map, unsigned int reg,
-                            const void *val, size_t val_len)
+                            const void *val, size_t val_len, bool async)
 {
        struct regmap_range_node *range;
+       unsigned long flags;
        u8 *u8 = map->work_buf;
+       void *work_val = map->work_buf + map->format.reg_bytes +
+               map->format.pad_bytes;
        void *buf;
        int ret = -ENOTSUPP;
        size_t len;
        int i;
 
+       BUG_ON(!map->bus);
+
        /* Check for unwritable registers before we start */
        if (map->writeable_reg)
                for (i = 0; i < val_len / map->format.val_bytes; i++)
@@ -918,7 +971,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
                        dev_dbg(map->dev, "Writing window %d/%zu\n",
                                win_residue, val_len / map->format.val_bytes);
                        ret = _regmap_raw_write(map, reg, val, win_residue *
-                                               map->format.val_bytes);
+                                               map->format.val_bytes, async);
                        if (ret != 0)
                                return ret;
 
@@ -941,6 +994,50 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
 
        u8[0] |= map->write_flag_mask;
 
+       if (async && map->bus->async_write) {
+               struct regmap_async *async = map->bus->async_alloc();
+               if (!async)
+                       return -ENOMEM;
+
+               async->work_buf = kzalloc(map->format.buf_size,
+                                         GFP_KERNEL | GFP_DMA);
+               if (!async->work_buf) {
+                       kfree(async);
+                       return -ENOMEM;
+               }
+
+               INIT_WORK(&async->cleanup, async_cleanup);
+               async->map = map;
+
+               /* If the caller supplied the value we can use it safely. */
+               memcpy(async->work_buf, map->work_buf, map->format.pad_bytes +
+                      map->format.reg_bytes + map->format.val_bytes);
+               if (val == work_val)
+                       val = async->work_buf + map->format.pad_bytes +
+                               map->format.reg_bytes;
+
+               spin_lock_irqsave(&map->async_lock, flags);
+               list_add_tail(&async->list, &map->async_list);
+               spin_unlock_irqrestore(&map->async_lock, flags);
+
+               ret = map->bus->async_write(map->bus_context, async->work_buf,
+                                           map->format.reg_bytes +
+                                           map->format.pad_bytes,
+                                           val, val_len, async);
+
+               if (ret != 0) {
+                       dev_err(map->dev, "Failed to schedule write: %d\n",
+                               ret);
+
+                       spin_lock_irqsave(&map->async_lock, flags);
+                       list_del(&async->list);
+                       spin_unlock_irqrestore(&map->async_lock, flags);
+
+                       kfree(async->work_buf);
+                       kfree(async);
+               }
+       }
+
        trace_regmap_hw_write_start(map->dev, reg,
                                    val_len / map->format.val_bytes);
 
@@ -948,8 +1045,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
         * send the work_buf directly, otherwise try to do a gather
         * write.
         */
-       if (val == (map->work_buf + map->format.pad_bytes +
-                   map->format.reg_bytes))
+       if (val == work_val)
                ret = map->bus->write(map->bus_context, map->work_buf,
                                      map->format.reg_bytes +
                                      map->format.pad_bytes +
@@ -981,14 +1077,62 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
        return ret;
 }
 
+static int _regmap_bus_formatted_write(void *context, unsigned int reg,
+                                      unsigned int val)
+{
+       int ret;
+       struct regmap_range_node *range;
+       struct regmap *map = context;
+
+       BUG_ON(!map->bus || !map->format.format_write);
+
+       range = _regmap_range_lookup(map, reg);
+       if (range) {
+               ret = _regmap_select_page(map, &reg, range, 1);
+               if (ret != 0)
+                       return ret;
+       }
+
+       map->format.format_write(map, reg, val);
+
+       trace_regmap_hw_write_start(map->dev, reg, 1);
+
+       ret = map->bus->write(map->bus_context, map->work_buf,
+                             map->format.buf_size);
+
+       trace_regmap_hw_write_done(map->dev, reg, 1);
+
+       return ret;
+}
+
+static int _regmap_bus_raw_write(void *context, unsigned int reg,
+                                unsigned int val)
+{
+       struct regmap *map = context;
+
+       BUG_ON(!map->bus || !map->format.format_val);
+
+       map->format.format_val(map->work_buf + map->format.reg_bytes
+                              + map->format.pad_bytes, val, 0);
+       return _regmap_raw_write(map, reg,
+                                map->work_buf +
+                                map->format.reg_bytes +
+                                map->format.pad_bytes,
+                                map->format.val_bytes, false);
+}
+
+static inline void *_regmap_map_get_context(struct regmap *map)
+{
+       return (map->bus) ? map : map->bus_context;
+}
+
 int _regmap_write(struct regmap *map, unsigned int reg,
                  unsigned int val)
 {
-       struct regmap_range_node *range;
        int ret;
-       BUG_ON(!map->format.format_write && !map->format.format_val);
+       void *context = _regmap_map_get_context(map);
 
-       if (!map->cache_bypass && map->format.format_write) {
+       if (!map->cache_bypass && !map->defer_caching) {
                ret = regcache_write(map, reg, val);
                if (ret != 0)
                        return ret;
@@ -1005,33 +1149,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
 
        trace_regmap_reg_write(map->dev, reg, val);
 
-       if (map->format.format_write) {
-               range = _regmap_range_lookup(map, reg);
-               if (range) {
-                       ret = _regmap_select_page(map, &reg, range, 1);
-                       if (ret != 0)
-                               return ret;
-               }
-
-               map->format.format_write(map, reg, val);
-
-               trace_regmap_hw_write_start(map->dev, reg, 1);
-
-               ret = map->bus->write(map->bus_context, 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
-                                      + map->format.pad_bytes, val, 0);
-               return _regmap_raw_write(map, reg,
-                                        map->work_buf +
-                                        map->format.reg_bytes +
-                                        map->format.pad_bytes,
-                                        map->format.val_bytes);
-       }
+       return map->reg_write(context, reg, val);
 }
 
 /**
@@ -1082,6 +1200,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
 {
        int ret;
 
+       if (!map->bus)
+               return -EINVAL;
        if (val_len % map->format.val_bytes)
                return -EINVAL;
        if (reg % map->reg_stride)
@@ -1089,7 +1209,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
 
        map->lock(map->lock_arg);
 
-       ret = _regmap_raw_write(map, reg, val, val_len);
+       ret = _regmap_raw_write(map, reg, val, val_len, false);
 
        map->unlock(map->lock_arg);
 
@@ -1118,6 +1238,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
        size_t val_bytes = map->format.val_bytes;
        void *wval;
 
+       if (!map->bus)
+               return -EINVAL;
        if (!map->format.parse_val)
                return -EINVAL;
        if (reg % map->reg_stride)
@@ -1145,14 +1267,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
        if (map->use_single_rw) {
                for (i = 0; i < val_count; i++) {
                        ret = regmap_raw_write(map,
-                                               reg + (i * map->reg_stride),
-                                               val + (i * val_bytes),
-                                               val_bytes);
+                                              reg + (i * map->reg_stride),
+                                              val + (i * val_bytes),
+                                              val_bytes);
                        if (ret != 0)
                                return ret;
                }
        } else {
-               ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
+               ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count,
+                                       false);
        }
 
        if (val_bytes != 1)
@@ -1164,6 +1287,48 @@ out:
 }
 EXPORT_SYMBOL_GPL(regmap_bulk_write);
 
+/**
+ * regmap_raw_write_async(): Write raw values to one or more registers
+ *                           asynchronously
+ *
+ * @map: Register map to write to
+ * @reg: Initial register to write to
+ * @val: Block of data to be written, laid out for direct transmission to the
+ *       device.  Must be valid until regmap_async_complete() is called.
+ * @val_len: Length of data pointed to by val.
+ *
+ * This function is intended to be used for things like firmware
+ * download where a large block of data needs to be transferred to the
+ * device.  No formatting will be done on the data provided.
+ *
+ * If supported by the underlying bus the write will be scheduled
+ * asynchronously, helping maximise I/O speed on higher speed buses
+ * like SPI.  regmap_async_complete() can be called to ensure that all
+ * asynchrnous writes have been completed.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_raw_write_async(struct regmap *map, unsigned int reg,
+                          const void *val, size_t val_len)
+{
+       int ret;
+
+       if (val_len % map->format.val_bytes)
+               return -EINVAL;
+       if (reg % map->reg_stride)
+               return -EINVAL;
+
+       map->lock(map->lock_arg);
+
+       ret = _regmap_raw_write(map, reg, val, val_len, true);
+
+       map->unlock(map->lock_arg);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_raw_write_async);
+
 static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
                            unsigned int val_len)
 {
@@ -1171,6 +1336,8 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
        u8 *u8 = map->work_buf;
        int ret;
 
+       BUG_ON(!map->bus);
+
        range = _regmap_range_lookup(map, reg);
        if (range) {
                ret = _regmap_select_page(map, &reg, range,
@@ -1202,10 +1369,29 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
        return ret;
 }
 
+static int _regmap_bus_read(void *context, unsigned int reg,
+                           unsigned int *val)
+{
+       int ret;
+       struct regmap *map = context;
+
+       if (!map->format.parse_val)
+               return -EINVAL;
+
+       ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
+       if (ret == 0)
+               *val = map->format.parse_val(map->work_buf);
+
+       return ret;
+}
+
 static int _regmap_read(struct regmap *map, unsigned int reg,
                        unsigned int *val)
 {
        int ret;
+       void *context = _regmap_map_get_context(map);
+
+       BUG_ON(!map->reg_read);
 
        if (!map->cache_bypass) {
                ret = regcache_read(map, reg, val);
@@ -1213,26 +1399,21 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
                        return 0;
        }
 
-       if (!map->format.parse_val)
-               return -EINVAL;
-
        if (map->cache_only)
                return -EBUSY;
 
-       ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
+       ret = map->reg_read(context, reg, val);
        if (ret == 0) {
-               *val = map->format.parse_val(map->work_buf);
-
 #ifdef LOG_DEVICE
                if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
                        dev_info(map->dev, "%x => %x\n", reg, *val);
 #endif
 
                trace_regmap_reg_read(map->dev, reg, *val);
-       }
 
-       if (ret == 0 && !map->cache_bypass)
-               regcache_write(map, reg, *val);
+               if (!map->cache_bypass)
+                       regcache_write(map, reg, *val);
+       }
 
        return ret;
 }
@@ -1283,6 +1464,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
        unsigned int v;
        int ret, i;
 
+       if (!map->bus)
+               return -EINVAL;
        if (val_len % map->format.val_bytes)
                return -EINVAL;
        if (reg % map->reg_stride)
@@ -1334,6 +1517,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
        size_t val_bytes = map->format.val_bytes;
        bool vol = regmap_volatile_range(map, reg, val_count);
 
+       if (!map->bus)
+               return -EINVAL;
        if (!map->format.parse_val)
                return -EINVAL;
        if (reg % map->reg_stride)
@@ -1450,6 +1635,68 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
 }
 EXPORT_SYMBOL_GPL(regmap_update_bits_check);
 
+void regmap_async_complete_cb(struct regmap_async *async, int ret)
+{
+       struct regmap *map = async->map;
+       bool wake;
+
+       spin_lock(&map->async_lock);
+
+       list_del(&async->list);
+       wake = list_empty(&map->async_list);
+
+       if (ret != 0)
+               map->async_ret = ret;
+
+       spin_unlock(&map->async_lock);
+
+       schedule_work(&async->cleanup);
+
+       if (wake)
+               wake_up(&map->async_waitq);
+}
+EXPORT_SYMBOL_GPL(regmap_async_complete_cb);
+
+static int regmap_async_is_done(struct regmap *map)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&map->async_lock, flags);
+       ret = list_empty(&map->async_list);
+       spin_unlock_irqrestore(&map->async_lock, flags);
+
+       return ret;
+}
+
+/**
+ * regmap_async_complete: Ensure all asynchronous I/O has completed.
+ *
+ * @map: Map to operate on.
+ *
+ * Blocks until any pending asynchronous I/O has completed.  Returns
+ * an error code for any failed I/O operations.
+ */
+int regmap_async_complete(struct regmap *map)
+{
+       unsigned long flags;
+       int ret;
+
+       /* Nothing to do with no async support */
+       if (!map->bus->async_write)
+               return 0;
+
+       wait_event(map->async_waitq, regmap_async_is_done(map));
+
+       spin_lock_irqsave(&map->async_lock, flags);
+       ret = map->async_ret;
+       map->async_ret = 0;
+       spin_unlock_irqrestore(&map->async_lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_async_complete);
+
 /**
  * regmap_register_patch: Register and apply register updates to be applied
  *                        on device initialistion
index 1133a64..f6fcb87 100644 (file)
@@ -96,6 +96,7 @@ const struct regmap_irq_chip wm5102_aod = {
        .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
        .ack_base = ARIZONA_AOD_IRQ1,
        .wake_base = ARIZONA_WAKE_CONTROL,
+       .wake_invert = 1,
        .num_regs = 1,
        .irqs = wm5102_aod_irqs,
        .num_irqs = ARRAY_SIZE(wm5102_aod_irqs),
index adda6b1..c415998 100644 (file)
@@ -255,6 +255,7 @@ const struct regmap_irq_chip wm5110_aod = {
        .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
        .ack_base = ARIZONA_AOD_IRQ1,
        .wake_base = ARIZONA_WAKE_CONTROL,
+       .wake_invert = 1,
        .num_regs = 1,
        .irqs = wm5110_aod_irqs,
        .num_irqs = ARRAY_SIZE(wm5110_aod_irqs),
index b7e95bf..bf77dfd 100644 (file)
@@ -28,7 +28,8 @@ struct regmap_range_cfg;
 enum regcache_type {
        REGCACHE_NONE,
        REGCACHE_RBTREE,
-       REGCACHE_COMPRESSED
+       REGCACHE_COMPRESSED,
+       REGCACHE_FLAT,
 };
 
 /**
@@ -127,7 +128,18 @@ typedef void (*regmap_unlock)(void *);
  * @lock_arg:    this field is passed as the only argument of lock/unlock
  *               functions (ignored in case regular lock/unlock functions
  *               are not overridden).
- *
+ * @reg_read:    Optional callback that if filled will be used to perform
+ *               all the reads from the registers. Should only be provided for
+ *               devices whos read operation cannot be represented as a simple read
+ *               operation on a bus such as SPI, I2C, etc. Most of the devices do
+ *               not need this.
+ * @reg_write:   Same as above for writing.
+ * @fast_io:     Register IO is fast. Use a spinlock instead of a mutex
+ *               to perform locking. This field is ignored if custom lock/unlock
+ *               functions are used (see fields lock/unlock of struct regmap_config).
+ *               This field is a duplicate of a similar file in
+ *               'struct regmap_bus' and serves exact same purpose.
+ *                Use it only for "no-bus" cases.
  * @max_register: Optional, specifies the maximum valid register index.
  * @wr_table:     Optional, points to a struct regmap_access_table specifying
  *                valid ranges for write access.
@@ -177,6 +189,11 @@ struct regmap_config {
        regmap_unlock unlock;
        void *lock_arg;
 
+       int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
+       int (*reg_write)(void *context, unsigned int reg, unsigned int val);
+
+       bool fast_io;
+
        unsigned int max_register;
        const struct regmap_access_table *wr_table;
        const struct regmap_access_table *rd_table;
@@ -235,14 +252,21 @@ struct regmap_range_cfg {
        unsigned int window_len;
 };
 
+struct regmap_async;
+
 typedef int (*regmap_hw_write)(void *context, const void *data,
                               size_t count);
 typedef int (*regmap_hw_gather_write)(void *context,
                                      const void *reg, size_t reg_len,
                                      const void *val, size_t val_len);
+typedef int (*regmap_hw_async_write)(void *context,
+                                    const void *reg, size_t reg_len,
+                                    const void *val, size_t val_len,
+                                    struct regmap_async *async);
 typedef int (*regmap_hw_read)(void *context,
                              const void *reg_buf, size_t reg_size,
                              void *val_buf, size_t val_size);
+typedef struct regmap_async *(*regmap_hw_async_alloc)(void);
 typedef void (*regmap_hw_free_context)(void *context);
 
 /**
@@ -255,8 +279,11 @@ typedef void (*regmap_hw_free_context)(void *context);
  * @write: Write operation.
  * @gather_write: Write operation with split register/value, return -ENOTSUPP
  *                if not implemented  on a given device.
+ * @async_write: Write operation which completes asynchronously, optional and
+ *               must serialise with respect to non-async I/O.
  * @read: Read operation.  Data is returned in the buffer used to transmit
  *         data.
+ * @async_alloc: Allocate a regmap_async() structure.
  * @read_flag_mask: Mask to be set in the top byte of the register when doing
  *                  a read.
  * @reg_format_endian_default: Default endianness for formatted register
@@ -265,13 +292,16 @@ typedef void (*regmap_hw_free_context)(void *context);
  * @val_format_endian_default: Default endianness for formatted register
  *     values. Used when the regmap_config specifies DEFAULT. If this is
  *     DEFAULT, BIG is assumed.
+ * @async_size: Size of struct used for async work.
  */
 struct regmap_bus {
        bool fast_io;
        regmap_hw_write write;
        regmap_hw_gather_write gather_write;
+       regmap_hw_async_write async_write;
        regmap_hw_read read;
        regmap_hw_free_context free_context;
+       regmap_hw_async_alloc async_alloc;
        u8 read_flag_mask;
        enum regmap_endian reg_format_endian_default;
        enum regmap_endian val_format_endian_default;
@@ -285,9 +315,9 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c,
                               const struct regmap_config *config);
 struct regmap *regmap_init_spi(struct spi_device *dev,
                               const struct regmap_config *config);
-struct regmap *regmap_init_mmio(struct device *dev,
-                               void __iomem *regs,
-                               const struct regmap_config *config);
+struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
+                                   void __iomem *regs,
+                                   const struct regmap_config *config);
 
 struct regmap *devm_regmap_init(struct device *dev,
                                const struct regmap_bus *bus,
@@ -297,9 +327,44 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
                                    const struct regmap_config *config);
 struct regmap *devm_regmap_init_spi(struct spi_device *dev,
                                    const struct regmap_config *config);
-struct regmap *devm_regmap_init_mmio(struct device *dev,
-                                    void __iomem *regs,
-                                    const struct regmap_config *config);
+struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
+                                        void __iomem *regs,
+                                        const struct regmap_config *config);
+
+/**
+ * regmap_init_mmio(): Initialise register map
+ *
+ * @dev: Device that will be interacted with
+ * @regs: Pointer to memory-mapped IO region
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+static inline struct regmap *regmap_init_mmio(struct device *dev,
+                                       void __iomem *regs,
+                                       const struct regmap_config *config)
+{
+       return regmap_init_mmio_clk(dev, NULL, regs, config);
+}
+
+/**
+ * devm_regmap_init_mmio(): Initialise managed register map
+ *
+ * @dev: Device that will be interacted with
+ * @regs: Pointer to memory-mapped IO region
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap.  The regmap will be automatically freed by the
+ * device management code.
+ */
+static inline struct regmap *devm_regmap_init_mmio(struct device *dev,
+                                       void __iomem *regs,
+                                       const struct regmap_config *config)
+{
+       return devm_regmap_init_mmio_clk(dev, NULL, regs, config);
+}
 
 void regmap_exit(struct regmap *map);
 int regmap_reinit_cache(struct regmap *map,
@@ -310,6 +375,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
                     const void *val, size_t val_len);
 int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
                        size_t val_count);
+int regmap_raw_write_async(struct regmap *map, unsigned int reg,
+                          const void *val, size_t val_len);
 int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
 int regmap_raw_read(struct regmap *map, unsigned int reg,
                    void *val, size_t val_len);
@@ -321,6 +388,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
                             unsigned int mask, unsigned int val,
                             bool *change);
 int regmap_get_val_bytes(struct regmap *map);
+int regmap_async_complete(struct regmap *map);
 
 int regcache_sync(struct regmap *map);
 int regcache_sync_region(struct regmap *map, unsigned int min,
@@ -381,6 +449,7 @@ struct regmap_irq_chip {
        unsigned int wake_base;
        unsigned int irq_reg_stride;
        unsigned int mask_invert;
+       unsigned int wake_invert;
        bool runtime_pm;
 
        int num_regs;
@@ -422,6 +491,13 @@ static inline int regmap_raw_write(struct regmap *map, unsigned int reg,
        return -EINVAL;
 }
 
+static inline int regmap_raw_write_async(struct regmap *map, unsigned int reg,
+                                        const void *val, size_t val_len)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+       return -EINVAL;
+}
+
 static inline int regmap_bulk_write(struct regmap *map, unsigned int reg,
                                    const void *val, size_t val_count)
 {
@@ -500,6 +576,11 @@ static inline void regcache_mark_dirty(struct regmap *map)
        WARN_ONCE(1, "regmap API is disabled");
 }
 
+static inline void regmap_async_complete(struct regmap *map)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+}
+
 static inline int regmap_register_patch(struct regmap *map,
                                        const struct reg_default *regs,
                                        int num_regs)