]> Pileus Git - ~andy/linux/commitdiff
Merge branch 'topic/cache' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Mon, 5 Dec 2011 13:18:50 +0000 (13:18 +0000)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Mon, 5 Dec 2011 13:18:50 +0000 (13:18 +0000)
drivers/base/regmap/Kconfig
drivers/base/regmap/Makefile
drivers/base/regmap/internal.h
drivers/base/regmap/regcache-indexed.c [deleted file]
drivers/base/regmap/regcache-lzo.c
drivers/base/regmap/regcache-rbtree.c
drivers/base/regmap/regcache.c
drivers/base/regmap/regmap-irq.c [new file with mode: 0644]
drivers/base/regmap/regmap.c
include/linux/regmap.h
include/trace/events/regmap.h

index 2fc6a66f39a41598bc15c7f1a5d9335d78643460..0f6c7fb418e872e05bdbc5bf6c075e6390fb82c4 100644 (file)
@@ -13,3 +13,6 @@ config REGMAP_I2C
 
 config REGMAP_SPI
        tristate
+
+config REGMAP_IRQ
+       bool
index 0573c8a9dacb895f58d7165cc7c5a4dbdf1dadaf..defd57963c84ec5d91ecb756317bb385ae1e3210 100644 (file)
@@ -1,4 +1,6 @@
-obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o regcache-lzo.o
+obj-$(CONFIG_REGMAP) += regmap.o regcache.o
+obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o
 obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
 obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
 obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
+obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o
index 6483e0bda0cfb97a48489f13a30e55c36c893e47..1a02b7537c8bbac9d59d21ee51c1361dd03c01c0 100644 (file)
@@ -106,7 +106,7 @@ static inline void regmap_debugfs_exit(struct regmap *map) { }
 #endif
 
 /* regcache core declarations */
-int regcache_init(struct regmap *map);
+int regcache_init(struct regmap *map, const struct regmap_config *config);
 void regcache_exit(struct regmap *map);
 int regcache_read(struct regmap *map,
                       unsigned int reg, unsigned int *value);
@@ -119,10 +119,7 @@ unsigned int regcache_get_val(const void *base, unsigned int idx,
 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;
 
diff --git a/drivers/base/regmap/regcache-indexed.c b/drivers/base/regmap/regcache-indexed.c
deleted file mode 100644 (file)
index 507731a..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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
-};
index 854448d09293e73e976f65745ff0b5cf0a0bc2b1..b7d16143edeb19b27ffe227535afdc9a555890d5 100644 (file)
@@ -15,6 +15,8 @@
 
 #include "internal.h"
 
+static int regcache_lzo_exit(struct regmap *map);
+
 struct regcache_lzo_ctx {
        void *wmem;
        void *dst;
@@ -27,7 +29,7 @@ struct regcache_lzo_ctx {
 };
 
 #define LZO_BLOCK_NUM 8
-static int regcache_lzo_block_count(void)
+static int regcache_lzo_block_count(struct regmap *map)
 {
        return LZO_BLOCK_NUM;
 }
@@ -106,19 +108,22 @@ 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());
+               DIV_ROUND_UP(map->cache_size_raw,
+                            regcache_lzo_block_count(map));
 }
 
 static inline int regcache_lzo_get_blkpos(struct regmap *map,
                                          unsigned int reg)
 {
-       return reg % (DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count()) /
+       return reg % (DIV_ROUND_UP(map->cache_size_raw,
+                                  regcache_lzo_block_count(map)) /
                      map->cache_word_size);
 }
 
 static inline int regcache_lzo_get_blksize(struct regmap *map)
 {
-       return DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count());
+       return DIV_ROUND_UP(map->cache_size_raw,
+                           regcache_lzo_block_count(map));
 }
 
 static int regcache_lzo_init(struct regmap *map)
@@ -131,7 +136,7 @@ static int regcache_lzo_init(struct regmap *map)
 
        ret = 0;
 
-       blkcount = regcache_lzo_block_count();
+       blkcount = regcache_lzo_block_count(map);
        map->cache = kzalloc(blkcount * sizeof *lzo_blocks,
                             GFP_KERNEL);
        if (!map->cache)
@@ -190,7 +195,7 @@ static int regcache_lzo_init(struct regmap *map)
 
        return 0;
 err:
-       regcache_exit(map);
+       regcache_lzo_exit(map);
        return ret;
 }
 
@@ -203,7 +208,7 @@ static int regcache_lzo_exit(struct regmap *map)
        if (!lzo_blocks)
                return 0;
 
-       blkcount = regcache_lzo_block_count();
+       blkcount = regcache_lzo_block_count(map);
        /*
         * the pointer to the bitmap used for syncing the cache
         * is shared amongst all lzo_blocks.  Ensure it is freed
index e31498499b0fdc86df83b653950769ec6c814e31..32620c4f16834112ab88c9f9741750aee94f26d6 100644 (file)
  */
 
 #include <linux/slab.h>
+#include <linux/debugfs.h>
 #include <linux/rbtree.h>
+#include <linux/seq_file.h>
 
 #include "internal.h"
 
 static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
                                 unsigned int value);
+static int regcache_rbtree_exit(struct regmap *map);
 
 struct regcache_rbtree_node {
        /* the actual rbtree node holding this block */
@@ -124,6 +127,60 @@ static int regcache_rbtree_insert(struct rb_root *root,
        return 1;
 }
 
+#ifdef CONFIG_DEBUG_FS
+static int rbtree_show(struct seq_file *s, void *ignored)
+{
+       struct regmap *map = s->private;
+       struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
+       struct regcache_rbtree_node *n;
+       struct rb_node *node;
+       unsigned int base, top;
+       int nodes = 0;
+       int registers = 0;
+
+       mutex_lock(&map->lock);
+
+       for (node = rb_first(&rbtree_ctx->root); node != NULL;
+            node = rb_next(node)) {
+               n = container_of(node, struct regcache_rbtree_node, node);
+
+               regcache_rbtree_get_base_top_reg(n, &base, &top);
+               seq_printf(s, "%x-%x (%d)\n", base, top, top - base + 1);
+
+               nodes++;
+               registers += top - base + 1;
+       }
+
+       seq_printf(s, "%d nodes, %d registers, average %d registers\n",
+                  nodes, registers, registers / nodes);
+
+       mutex_unlock(&map->lock);
+
+       return 0;
+}
+
+static int rbtree_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, rbtree_show, inode->i_private);
+}
+
+static const struct file_operations rbtree_fops = {
+       .open           = rbtree_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void rbtree_debugfs_init(struct regmap *map)
+{
+       debugfs_create_file("rbtree", 0400, map->debugfs, map, &rbtree_fops);
+}
+#else
+static void rbtree_debugfs_init(struct regmap *map)
+{
+}
+#endif
+
 static int regcache_rbtree_init(struct regmap *map)
 {
        struct regcache_rbtree_ctx *rbtree_ctx;
@@ -146,10 +203,12 @@ static int regcache_rbtree_init(struct regmap *map)
                        goto err;
        }
 
+       rbtree_debugfs_init(map);
+
        return 0;
 
 err:
-       regcache_exit(map);
+       regcache_rbtree_exit(map);
        return ret;
 }
 
index 6ab9f0384d82c3fa82f6c575d45dac31d4b305ca..1ead66186b7c0a90586f223b0b9bc23d3770a3ed 100644 (file)
@@ -19,7 +19,6 @@
 #include "internal.h"
 
 static const struct regcache_ops *cache_types[] = {
-       &regcache_indexed_ops,
        &regcache_rbtree_ops,
        &regcache_lzo_ops,
 };
@@ -61,8 +60,10 @@ static int regcache_hw_init(struct regmap *map)
 
        map->reg_defaults = kmalloc(count * sizeof(struct reg_default),
                                      GFP_KERNEL);
-       if (!map->reg_defaults)
-               return -ENOMEM;
+       if (!map->reg_defaults) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
 
        /* fill the reg_defaults */
        map->num_reg_defaults = count;
@@ -77,9 +78,15 @@ static int regcache_hw_init(struct regmap *map)
        }
 
        return 0;
+
+err_free:
+       if (map->cache_free)
+               kfree(map->reg_defaults_raw);
+
+       return ret;
 }
 
-int regcache_init(struct regmap *map)
+int regcache_init(struct regmap *map, const struct regmap_config *config)
 {
        int ret;
        int i;
@@ -100,6 +107,12 @@ int regcache_init(struct regmap *map)
                return -EINVAL;
        }
 
+       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_word_size = DIV_ROUND_UP(config->val_bits, 8);
+       map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw;
+
        map->cache = NULL;
        map->cache_ops = cache_types[i];
 
@@ -112,10 +125,10 @@ int regcache_init(struct regmap *map)
         * won't vanish from under us.  We'll need to make
         * a copy of it.
         */
-       if (map->reg_defaults) {
+       if (config->reg_defaults) {
                if (!map->num_reg_defaults)
                        return -EINVAL;
-               tmp_buf = kmemdup(map->reg_defaults, map->num_reg_defaults *
+               tmp_buf = kmemdup(config->reg_defaults, map->num_reg_defaults *
                                  sizeof(struct reg_default), GFP_KERNEL);
                if (!tmp_buf)
                        return -ENOMEM;
@@ -136,9 +149,18 @@ int regcache_init(struct regmap *map)
        if (map->cache_ops->init) {
                dev_dbg(map->dev, "Initializing %s cache\n",
                        map->cache_ops->name);
-               return map->cache_ops->init(map);
+               ret = map->cache_ops->init(map);
+               if (ret)
+                       goto err_free;
        }
        return 0;
+
+err_free:
+       kfree(map->reg_defaults);
+       if (map->cache_free)
+               kfree(map->reg_defaults_raw);
+
+       return ret;
 }
 
 void regcache_exit(struct regmap *map)
@@ -171,16 +193,21 @@ void regcache_exit(struct regmap *map)
 int regcache_read(struct regmap *map,
                  unsigned int reg, unsigned int *value)
 {
+       int ret;
+
        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)) {
+               ret = map->cache_ops->read(map, reg, value);
 
-       if (!regmap_volatile(map, reg))
-               return map->cache_ops->read(map, reg, value);
+               if (ret == 0)
+                       trace_regmap_reg_read_cache(map->dev, reg, *value);
+
+               return ret;
+       }
 
        return -EINVAL;
 }
@@ -400,22 +427,3 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg)
        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-irq.c b/drivers/base/regmap/regmap-irq.c
new file mode 100644 (file)
index 0000000..6b8a74c
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * regmap based irq_chip
+ *
+ * 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/export.h>
+#include <linux/regmap.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "internal.h"
+
+struct regmap_irq_chip_data {
+       struct mutex lock;
+
+       struct regmap *map;
+       struct regmap_irq_chip *chip;
+
+       int irq_base;
+
+       void *status_reg_buf;
+       unsigned int *status_buf;
+       unsigned int *mask_buf;
+       unsigned int *mask_buf_def;
+};
+
+static inline const
+struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,
+                                    int irq)
+{
+       return &data->chip->irqs[irq - data->irq_base];
+}
+
+static void regmap_irq_lock(struct irq_data *data)
+{
+       struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
+
+       mutex_lock(&d->lock);
+}
+
+static void regmap_irq_sync_unlock(struct irq_data *data)
+{
+       struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
+       int i, ret;
+
+       /*
+        * If there's been a change in the mask write it back to the
+        * hardware.  We rely on the use of the regmap core cache to
+        * suppress pointless writes.
+        */
+       for (i = 0; i < d->chip->num_regs; i++) {
+               ret = regmap_update_bits(d->map, d->chip->mask_base + i,
+                                        d->mask_buf_def[i], d->mask_buf[i]);
+               if (ret != 0)
+                       dev_err(d->map->dev, "Failed to sync masks in %x\n",
+                               d->chip->mask_base + i);
+       }
+
+       mutex_unlock(&d->lock);
+}
+
+static void regmap_irq_enable(struct irq_data *data)
+{
+       struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
+       const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq);
+
+       d->mask_buf[irq_data->reg_offset] &= ~irq_data->mask;
+}
+
+static void regmap_irq_disable(struct irq_data *data)
+{
+       struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
+       const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq);
+
+       d->mask_buf[irq_data->reg_offset] |= irq_data->mask;
+}
+
+static struct irq_chip regmap_irq_chip = {
+       .name                   = "regmap",
+       .irq_bus_lock           = regmap_irq_lock,
+       .irq_bus_sync_unlock    = regmap_irq_sync_unlock,
+       .irq_disable            = regmap_irq_disable,
+       .irq_enable             = regmap_irq_enable,
+};
+
+static irqreturn_t regmap_irq_thread(int irq, void *d)
+{
+       struct regmap_irq_chip_data *data = d;
+       struct regmap_irq_chip *chip = data->chip;
+       struct regmap *map = data->map;
+       int ret, i;
+       u8 *buf8 = data->status_reg_buf;
+       u16 *buf16 = data->status_reg_buf;
+       u32 *buf32 = data->status_reg_buf;
+       bool handled = false;
+
+       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);
+               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++) {
+               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;
+               }
+
+               data->status_buf[i] &= ~data->mask_buf[i];
+
+               if (data->status_buf[i] && chip->ack_base) {
+                       ret = regmap_write(map, chip->ack_base + i,
+                                          data->status_buf[i]);
+                       if (ret != 0)
+                               dev_err(map->dev, "Failed to ack 0x%x: %d\n",
+                                       chip->ack_base + i, ret);
+               }
+       }
+
+       for (i = 0; i < chip->num_irqs; i++) {
+               if (data->status_buf[chip->irqs[i].reg_offset] &
+                   chip->irqs[i].mask) {
+                       handle_nested_irq(data->irq_base + i);
+                       handled = true;
+               }
+       }
+
+       if (handled)
+               return IRQ_HANDLED;
+       else
+               return IRQ_NONE;
+}
+
+/**
+ * regmap_add_irq_chip(): Use standard regmap IRQ controller handling
+ *
+ * map:       The regmap for the device.
+ * irq:       The IRQ the device uses to signal interrupts
+ * irq_flags: The IRQF_ flags to use for the primary interrupt.
+ * chip:      Configuration for the interrupt controller.
+ * data:      Runtime data structure for the controller, allocated on success
+ *
+ * Returns 0 on success or an errno on failure.
+ *
+ * In order for this to be efficient the chip really should use a
+ * register cache.  The chip driver is responsible for restoring the
+ * register values used by the IRQ controller over suspend and resume.
+ */
+int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
+                       int irq_base, struct regmap_irq_chip *chip,
+                       struct regmap_irq_chip_data **data)
+{
+       struct regmap_irq_chip_data *d;
+       int cur_irq, i;
+       int ret = -ENOMEM;
+
+       irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
+       if (irq_base < 0) {
+               dev_warn(map->dev, "Failed to allocate IRQs: %d\n",
+                        irq_base);
+               return irq_base;
+       }
+
+       d = kzalloc(sizeof(*d), GFP_KERNEL);
+       if (!d)
+               return -ENOMEM;
+
+       d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
+                               GFP_KERNEL);
+       if (!d->status_buf)
+               goto err_alloc;
+
+       d->status_reg_buf = kzalloc(map->format.val_bytes * chip->num_regs,
+                                   GFP_KERNEL);
+       if (!d->status_reg_buf)
+               goto err_alloc;
+
+       d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
+                             GFP_KERNEL);
+       if (!d->mask_buf)
+               goto err_alloc;
+
+       d->mask_buf_def = kzalloc(sizeof(unsigned int) * chip->num_regs,
+                                 GFP_KERNEL);
+       if (!d->mask_buf_def)
+               goto err_alloc;
+
+       d->map = map;
+       d->chip = chip;
+       d->irq_base = irq_base;
+       mutex_init(&d->lock);
+
+       for (i = 0; i < chip->num_irqs; i++)
+               d->mask_buf_def[chip->irqs[i].reg_offset]
+                       |= chip->irqs[i].mask;
+
+       /* Mask all the interrupts by default */
+       for (i = 0; i < chip->num_regs; i++) {
+               d->mask_buf[i] = d->mask_buf_def[i];
+               ret = regmap_write(map, chip->mask_base + i, d->mask_buf[i]);
+               if (ret != 0) {
+                       dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
+                               chip->mask_base + i, ret);
+                       goto err_alloc;
+               }
+       }
+
+       /* Register them with genirq */
+       for (cur_irq = irq_base;
+            cur_irq < chip->num_irqs + irq_base;
+            cur_irq++) {
+               irq_set_chip_data(cur_irq, d);
+               irq_set_chip_and_handler(cur_irq, &regmap_irq_chip,
+                                        handle_edge_irq);
+               irq_set_nested_thread(cur_irq, 1);
+
+               /* ARM needs us to explicitly flag the IRQ as valid
+                * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+               set_irq_flags(cur_irq, IRQF_VALID);
+#else
+               irq_set_noprobe(cur_irq);
+#endif
+       }
+
+       ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags,
+                                  chip->name, d);
+       if (ret != 0) {
+               dev_err(map->dev, "Failed to request IRQ %d: %d\n", irq, ret);
+               goto err_alloc;
+       }
+
+       return 0;
+
+err_alloc:
+       kfree(d->mask_buf_def);
+       kfree(d->mask_buf);
+       kfree(d->status_reg_buf);
+       kfree(d->status_buf);
+       kfree(d);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_add_irq_chip);
+
+/**
+ * regmap_del_irq_chip(): Stop interrupt handling for a regmap IRQ chip
+ *
+ * @irq: Primary IRQ for the device
+ * @d:   regmap_irq_chip_data allocated by regmap_add_irq_chip()
+ */
+void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
+{
+       if (!d)
+               return;
+
+       free_irq(irq, d);
+       kfree(d->mask_buf_def);
+       kfree(d->mask_buf);
+       kfree(d->status_reg_buf);
+       kfree(d->status_buf);
+       kfree(d);
+}
+EXPORT_SYMBOL_GPL(regmap_del_irq_chip);
index 579e85b8a68458842b6cc918b316742ea0aa1554..be10a4ff660915625454375ce9fb30a97d5b7ef9 100644 (file)
@@ -64,6 +64,18 @@ bool regmap_precious(struct regmap *map, unsigned int reg)
        return false;
 }
 
+static bool regmap_volatile_range(struct regmap *map, unsigned int reg,
+       unsigned int num)
+{
+       unsigned int i;
+
+       for (i = 0; i < num; i++)
+               if (!regmap_volatile(map, reg + i))
+                       return false;
+
+       return true;
+}
+
 static void regmap_format_4_12_write(struct regmap *map,
                                     unsigned int reg, unsigned int val)
 {
@@ -78,6 +90,16 @@ static void regmap_format_7_9_write(struct regmap *map,
        *out = cpu_to_be16((reg << 9) | val);
 }
 
+static void regmap_format_10_14_write(struct regmap *map,
+                                   unsigned int reg, unsigned int val)
+{
+       u8 *out = map->work_buf;
+
+       out[2] = val;
+       out[1] = (val >> 8) | (reg << 6);
+       out[0] = reg >> 2;
+}
+
 static void regmap_format_8(void *buf, unsigned int val)
 {
        u8 *b = buf;
@@ -127,7 +149,7 @@ struct regmap *regmap_init(struct device *dev,
        int ret = -EINVAL;
 
        if (!bus || !config)
-               return NULL;
+               goto err;
 
        map = kzalloc(sizeof(*map), GFP_KERNEL);
        if (map == NULL) {
@@ -147,12 +169,6 @@ struct regmap *regmap_init(struct device *dev,
        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;
@@ -182,6 +198,16 @@ struct regmap *regmap_init(struct device *dev,
                }
                break;
 
+       case 10:
+               switch (config->val_bits) {
+               case 14:
+                       map->format.format_write = regmap_format_10_14_write;
+                       break;
+               default:
+                       goto err_map;
+               }
+               break;
+
        case 8:
                map->format.format_reg = regmap_format_8;
                break;
@@ -215,14 +241,16 @@ struct regmap *regmap_init(struct device *dev,
                goto err_map;
        }
 
-       ret = regcache_init(map);
-       if (ret < 0)
-               goto err_map;
-
        regmap_debugfs_init(map);
 
+       ret = regcache_init(map, config);
+       if (ret < 0)
+               goto err_free_workbuf;
+
        return map;
 
+err_free_workbuf:
+       kfree(map->work_buf);
 err_map:
        kfree(map);
 err:
@@ -410,9 +438,11 @@ EXPORT_SYMBOL_GPL(regmap_write);
 int regmap_raw_write(struct regmap *map, unsigned int reg,
                     const void *val, size_t val_len)
 {
+       size_t val_count = val_len / map->format.val_bytes;
        int ret;
 
-       WARN_ON(map->cache_type != REGCACHE_NONE);
+       WARN_ON(!regmap_volatile_range(map, reg, val_count) &&
+               map->cache_type != REGCACHE_NONE);
 
        mutex_lock(&map->lock);
 
@@ -457,15 +487,15 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
 {
        int ret;
 
-       if (!map->format.parse_val)
-               return -EINVAL;
-
        if (!map->cache_bypass) {
                ret = regcache_read(map, reg, val);
                if (ret == 0)
                        return 0;
        }
 
+       if (!map->format.parse_val)
+               return -EINVAL;
+
        if (map->cache_only)
                return -EBUSY;
 
@@ -516,15 +546,11 @@ EXPORT_SYMBOL_GPL(regmap_read);
 int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
                    size_t val_len)
 {
+       size_t val_count = val_len / map->format.val_bytes;
        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);
+       WARN_ON(!regmap_volatile_range(map, reg, val_count) &&
+               map->cache_type != REGCACHE_NONE);
 
        mutex_lock(&map->lock);
 
@@ -552,16 +578,11 @@ 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;
+       bool vol = regmap_volatile_range(map, reg, val_count);
 
        if (!map->format.parse_val)
                return -EINVAL;
 
-       /* 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)
@@ -581,40 +602,73 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
 }
 EXPORT_SYMBOL_GPL(regmap_bulk_read);
 
-/**
- * remap_update_bits: Perform a read/modify/write cycle on the register map
- *
- * @map: Register map to update
- * @reg: Register to update
- * @mask: Bitmask to change
- * @val: New value for bitmask
- *
- * Returns zero for success, a negative number on error.
- */
-int regmap_update_bits(struct regmap *map, unsigned int reg,
-                      unsigned int mask, unsigned int val)
+static int _regmap_update_bits(struct regmap *map, unsigned int reg,
+                              unsigned int mask, unsigned int val,
+                              bool *change)
 {
        int ret;
-       unsigned int tmp;
+       unsigned int tmp, orig;
 
        mutex_lock(&map->lock);
 
-       ret = _regmap_read(map, reg, &tmp);
+       ret = _regmap_read(map, reg, &orig);
        if (ret != 0)
                goto out;
 
-       tmp &= ~mask;
+       tmp = orig & ~mask;
        tmp |= val & mask;
 
-       ret = _regmap_write(map, reg, tmp);
+       if (tmp != orig) {
+               ret = _regmap_write(map, reg, tmp);
+               *change = true;
+       } else {
+               *change = false;
+       }
 
 out:
        mutex_unlock(&map->lock);
 
        return ret;
 }
+
+/**
+ * regmap_update_bits: Perform a read/modify/write cycle on the register map
+ *
+ * @map: Register map to update
+ * @reg: Register to update
+ * @mask: Bitmask to change
+ * @val: New value for bitmask
+ *
+ * Returns zero for success, a negative number on error.
+ */
+int regmap_update_bits(struct regmap *map, unsigned int reg,
+                      unsigned int mask, unsigned int val)
+{
+       bool change;
+       return _regmap_update_bits(map, reg, mask, val, &change);
+}
 EXPORT_SYMBOL_GPL(regmap_update_bits);
 
+/**
+ * regmap_update_bits_check: Perform a read/modify/write cycle on the
+ *                           register map and report if updated
+ *
+ * @map: Register map to update
+ * @reg: Register to update
+ * @mask: Bitmask to change
+ * @val: New value for bitmask
+ * @change: Boolean indicating if a write was done
+ *
+ * Returns zero for success, a negative number on error.
+ */
+int regmap_update_bits_check(struct regmap *map, unsigned int reg,
+                            unsigned int mask, unsigned int val,
+                            bool *change)
+{
+       return _regmap_update_bits(map, reg, mask, val, change);
+}
+EXPORT_SYMBOL_GPL(regmap_update_bits_check);
+
 static int __init regmap_initcall(void)
 {
        regmap_debugfs_initcall();
index 86923a98a7664e5dd3dcd1bfff0016bda56689e1..cfce3a358fbf114036f44af141f7e244034208ad 100644 (file)
@@ -23,7 +23,6 @@ struct spi_device;
 /* An enum of all the supported cache types */
 enum regcache_type {
        REGCACHE_NONE,
-       REGCACHE_INDEXED,
        REGCACHE_RBTREE,
        REGCACHE_COMPRESSED
 };
@@ -83,7 +82,7 @@ struct regmap_config {
        bool (*precious_reg)(struct device *dev, unsigned int reg);
 
        unsigned int max_register;
-       struct reg_default *reg_defaults;
+       const struct reg_default *reg_defaults;
        unsigned int num_reg_defaults;
        enum regcache_type cache_type;
        const void *reg_defaults_raw;
@@ -141,10 +140,60 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
                     size_t val_count);
 int regmap_update_bits(struct regmap *map, unsigned int reg,
                       unsigned int mask, unsigned int val);
+int regmap_update_bits_check(struct regmap *map, unsigned int reg,
+                            unsigned int mask, unsigned int val,
+                            bool *change);
 
 int regcache_sync(struct regmap *map);
 void regcache_cache_only(struct regmap *map, bool enable);
 void regcache_cache_bypass(struct regmap *map, bool enable);
 void regcache_mark_dirty(struct regmap *map);
 
+/**
+ * Description of an IRQ for the generic regmap irq_chip.
+ *
+ * @reg_offset: Offset of the status/mask register within the bank
+ * @mask:       Mask used to flag/control the register.
+ */
+struct regmap_irq {
+       unsigned int reg_offset;
+       unsigned int mask;
+};
+
+/**
+ * Description of a generic regmap irq_chip.  This is not intended to
+ * handle every possible interrupt controller, but it should handle a
+ * substantial proportion of those that are found in the wild.
+ *
+ * @name:        Descriptive name for IRQ controller.
+ *
+ * @status_base: Base status register address.
+ * @mask_base:   Base mask register address.
+ * @ack_base:    Base ack address.  If zero then the chip is clear on read.
+ *
+ * @num_regs:    Number of registers in each control bank.
+ * @irqs:        Descriptors for individual IRQs.  Interrupt numbers are
+ *               assigned based on the index in the array of the interrupt.
+ * @num_irqs:    Number of descriptors.
+ */
+struct regmap_irq_chip {
+       const char *name;
+
+       unsigned int status_base;
+       unsigned int mask_base;
+       unsigned int ack_base;
+
+       int num_regs;
+
+       const struct regmap_irq *irqs;
+       int num_irqs;
+};
+
+struct regmap_irq_chip_data;
+
+int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
+                       int irq_base, struct regmap_irq_chip *chip,
+                       struct regmap_irq_chip_data **data);
+void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data);
+
 #endif
index 1e3193b8fcc8f5863e665d91f287c4728f036346..12fbf43524e90e77759e80751bfe5ab983b4ad2d 100644 (file)
@@ -55,6 +55,15 @@ DEFINE_EVENT(regmap_reg, regmap_reg_read,
 
 );
 
+DEFINE_EVENT(regmap_reg, regmap_reg_read_cache,
+
+       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),