]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'regmap/topic/cache' into regmap-next
authorMark Brown <broonie@linaro.org>
Sun, 30 Jun 2013 11:40:01 +0000 (12:40 +0100)
committerMark Brown <broonie@linaro.org>
Sun, 30 Jun 2013 11:40:01 +0000 (12:40 +0100)
drivers/base/regmap/internal.h
drivers/base/regmap/regcache-rbtree.c
drivers/base/regmap/regcache.c
drivers/base/regmap/regmap-debugfs.c
drivers/base/regmap/regmap.c
include/linux/regmap.h
include/trace/events/regmap.h

index c130536e0ab0626866a41dd23f35d2c260bb9980..ae23d8391aa0f56caf719a9ea89a9d075a5be90d 100644 (file)
@@ -52,6 +52,7 @@ struct regmap_async {
 struct regmap {
        struct mutex mutex;
        spinlock_t spinlock;
+       unsigned long spinlock_flags;
        regmap_lock lock;
        regmap_unlock unlock;
        void *lock_arg; /* This is passed to lock/unlock functions */
@@ -148,6 +149,7 @@ struct regcache_ops {
        int (*read)(struct regmap *map, unsigned int reg, unsigned int *value);
        int (*write)(struct regmap *map, unsigned int reg, unsigned int value);
        int (*sync)(struct regmap *map, unsigned int min, unsigned int max);
+       int (*drop)(struct regmap *map, unsigned int min, unsigned int max);
 };
 
 bool regmap_writeable(struct regmap *map, unsigned int reg);
index 02f490bad30f791f627db78584ca75b691bc6eb7..5c1435c4e210c9311e01e92e9a0039ec89df2bdb 100644 (file)
@@ -304,6 +304,48 @@ static int regcache_rbtree_insert_to_block(struct regmap *map,
        return 0;
 }
 
+static struct regcache_rbtree_node *
+regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
+{
+       struct regcache_rbtree_node *rbnode;
+       const struct regmap_range *range;
+       int i;
+
+       rbnode = kzalloc(sizeof(*rbnode), GFP_KERNEL);
+       if (!rbnode)
+               return NULL;
+
+       /* If there is a read table then use it to guess at an allocation */
+       if (map->rd_table) {
+               for (i = 0; i < map->rd_table->n_yes_ranges; i++) {
+                       if (regmap_reg_in_range(reg,
+                                               &map->rd_table->yes_ranges[i]))
+                               break;
+               }
+
+               if (i != map->rd_table->n_yes_ranges) {
+                       range = &map->rd_table->yes_ranges[i];
+                       rbnode->blklen = range->range_max - range->range_min
+                               + 1;
+                       rbnode->base_reg = range->range_min;
+               }
+       }
+
+       if (!rbnode->blklen) {
+               rbnode->blklen = sizeof(*rbnode);
+               rbnode->base_reg = reg;
+       }
+
+       rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
+                               GFP_KERNEL);
+       if (!rbnode->block) {
+               kfree(rbnode);
+               return NULL;
+       }
+
+       return rbnode;
+}
+
 static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
                                 unsigned int value)
 {
@@ -354,23 +396,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
                                return 0;
                        }
                }
-               /* we did not manage to find a place to insert it in an existing
-                * block so create a new rbnode with a single register in its block.
-                * This block will get populated further if any other adjacent
-                * registers get modified in the future.
+
+               /* We did not manage to find a place to insert it in
+                * an existing block so create a new rbnode.
                 */
-               rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL);
+               rbnode = regcache_rbtree_node_alloc(map, reg);
                if (!rbnode)
                        return -ENOMEM;
-               rbnode->blklen = sizeof(*rbnode);
-               rbnode->base_reg = reg;
-               rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
-                                       GFP_KERNEL);
-               if (!rbnode->block) {
-                       kfree(rbnode);
-                       return -ENOMEM;
-               }
-               regcache_rbtree_set_register(map, rbnode, 0, value);
+               regcache_rbtree_set_register(map, rbnode,
+                                            reg - rbnode->base_reg, value);
                regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode);
                rbtree_ctx->cached_rbnode = rbnode;
        }
index 507ee2da0f6ee9b6455ab967f2dcf76a49c57b02..e69102696533e7224b74a1237f64c478eeb2ab18 100644 (file)
@@ -250,6 +250,38 @@ int regcache_write(struct regmap *map,
        return 0;
 }
 
+static int regcache_default_sync(struct regmap *map, unsigned int min,
+                                unsigned int max)
+{
+       unsigned int reg;
+
+       for (reg = min; reg <= max; reg++) {
+               unsigned int val;
+               int ret;
+
+               if (regmap_volatile(map, reg))
+                       continue;
+
+               ret = regcache_read(map, reg, &val);
+               if (ret)
+                       return ret;
+
+               /* Is this the hardware default?  If so skip. */
+               ret = regcache_lookup_reg(map, reg);
+               if (ret >= 0 && val == map->reg_defaults[ret].def)
+                       continue;
+
+               map->cache_bypass = 1;
+               ret = _regmap_write(map, reg, val);
+               map->cache_bypass = 0;
+               if (ret)
+                       return ret;
+               dev_dbg(map->dev, "Synced register %#x, value %#x\n", reg, val);
+       }
+
+       return 0;
+}
+
 /**
  * regcache_sync: Sync the register cache with the hardware.
  *
@@ -268,7 +300,7 @@ int regcache_sync(struct regmap *map)
        const char *name;
        unsigned int bypass;
 
-       BUG_ON(!map->cache_ops || !map->cache_ops->sync);
+       BUG_ON(!map->cache_ops);
 
        map->lock(map->lock_arg);
        /* Remember the initial bypass state */
@@ -297,7 +329,10 @@ int regcache_sync(struct regmap *map)
        }
        map->cache_bypass = 0;
 
-       ret = map->cache_ops->sync(map, 0, map->max_register);
+       if (map->cache_ops->sync)
+               ret = map->cache_ops->sync(map, 0, map->max_register);
+       else
+               ret = regcache_default_sync(map, 0, map->max_register);
 
        if (ret == 0)
                map->cache_dirty = false;
@@ -331,7 +366,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
        const char *name;
        unsigned int bypass;
 
-       BUG_ON(!map->cache_ops || !map->cache_ops->sync);
+       BUG_ON(!map->cache_ops);
 
        map->lock(map->lock_arg);
 
@@ -346,7 +381,10 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
        if (!map->cache_dirty)
                goto out;
 
-       ret = map->cache_ops->sync(map, min, max);
+       if (map->cache_ops->sync)
+               ret = map->cache_ops->sync(map, min, max);
+       else
+               ret = regcache_default_sync(map, min, max);
 
 out:
        trace_regcache_sync(map->dev, name, "stop region");
@@ -358,6 +396,43 @@ out:
 }
 EXPORT_SYMBOL_GPL(regcache_sync_region);
 
+/**
+ * regcache_drop_region: Discard part of the register cache
+ *
+ * @map: map to operate on
+ * @min: first register to discard
+ * @max: last register to discard
+ *
+ * Discard part of the register cache.
+ *
+ * Return a negative value on failure, 0 on success.
+ */
+int regcache_drop_region(struct regmap *map, unsigned int min,
+                        unsigned int max)
+{
+       unsigned int reg;
+       int ret = 0;
+
+       if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop))
+               return -EINVAL;
+
+       map->lock(map->lock_arg);
+
+       trace_regcache_drop_region(map->dev, min, max);
+
+       if (map->cache_present)
+               for (reg = min; reg < max + 1; reg++)
+                       clear_bit(reg, map->cache_present);
+
+       if (map->cache_ops && map->cache_ops->drop)
+               ret = map->cache_ops->drop(map, min, max);
+
+       map->unlock(map->lock_arg);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regcache_drop_region);
+
 /**
  * regcache_cache_only: Put a register map into cache only mode
  *
index 975719bc345008a4d396c72b5b1e160c50b8d2a8..98ee9c79678df514b5cc43045ee0ebfc60a3a71a 100644 (file)
@@ -84,6 +84,10 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
        unsigned int fpos_offset;
        unsigned int reg_offset;
 
+       /* Suppress the cache if we're using a subrange */
+       if (from)
+               return from;
+
        /*
         * If we don't have a cache build one so we don't have to do a
         * linear scan each time.
index a941dcfe7590119f7f1cc1b4a69574ea3d47b9b6..1a01553189b34e321ad4b9c9db031c72b7546dbd 100644 (file)
@@ -65,9 +65,8 @@ bool regmap_reg_in_ranges(unsigned int reg,
 }
 EXPORT_SYMBOL_GPL(regmap_reg_in_ranges);
 
-static bool _regmap_check_range_table(struct regmap *map,
-                                     unsigned int reg,
-                                     const struct regmap_access_table *table)
+bool regmap_check_range_table(struct regmap *map, unsigned int reg,
+                             const struct regmap_access_table *table)
 {
        /* Check "no ranges" first */
        if (regmap_reg_in_ranges(reg, table->no_ranges, table->n_no_ranges))
@@ -80,6 +79,7 @@ static bool _regmap_check_range_table(struct regmap *map,
        return regmap_reg_in_ranges(reg, table->yes_ranges,
                                    table->n_yes_ranges);
 }
+EXPORT_SYMBOL_GPL(regmap_check_range_table);
 
 bool regmap_writeable(struct regmap *map, unsigned int reg)
 {
@@ -90,7 +90,7 @@ bool regmap_writeable(struct regmap *map, unsigned int reg)
                return map->writeable_reg(map->dev, reg);
 
        if (map->wr_table)
-               return _regmap_check_range_table(map, reg, map->wr_table);
+               return regmap_check_range_table(map, reg, map->wr_table);
 
        return true;
 }
@@ -107,7 +107,7 @@ bool regmap_readable(struct regmap *map, unsigned int reg)
                return map->readable_reg(map->dev, reg);
 
        if (map->rd_table)
-               return _regmap_check_range_table(map, reg, map->rd_table);
+               return regmap_check_range_table(map, reg, map->rd_table);
 
        return true;
 }
@@ -121,7 +121,7 @@ bool regmap_volatile(struct regmap *map, unsigned int reg)
                return map->volatile_reg(map->dev, reg);
 
        if (map->volatile_table)
-               return _regmap_check_range_table(map, reg, map->volatile_table);
+               return regmap_check_range_table(map, reg, map->volatile_table);
 
        return true;
 }
@@ -135,7 +135,7 @@ bool regmap_precious(struct regmap *map, unsigned int reg)
                return map->precious_reg(map->dev, reg);
 
        if (map->precious_table)
-               return _regmap_check_range_table(map, reg, map->precious_table);
+               return regmap_check_range_table(map, reg, map->precious_table);
 
        return false;
 }
@@ -302,13 +302,16 @@ static void regmap_unlock_mutex(void *__map)
 static void regmap_lock_spinlock(void *__map)
 {
        struct regmap *map = __map;
-       spin_lock(&map->spinlock);
+       unsigned long flags;
+
+       spin_lock_irqsave(&map->spinlock, flags);
+       map->spinlock_flags = flags;
 }
 
 static void regmap_unlock_spinlock(void *__map)
 {
        struct regmap *map = __map;
-       spin_unlock(&map->spinlock);
+       spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags);
 }
 
 static void dev_get_regmap_release(struct device *dev, void *res)
index 02d84e24b7c2083ff1a4ddf13a1b2ac7ad663261..d6f3221e29d469372125da6381a85c80b14b5e99 100644 (file)
@@ -394,10 +394,15 @@ bool regmap_can_raw_write(struct regmap *map);
 int regcache_sync(struct regmap *map);
 int regcache_sync_region(struct regmap *map, unsigned int min,
                         unsigned int max);
+int regcache_drop_region(struct regmap *map, unsigned int min,
+                        unsigned int max);
 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);
 
+bool regmap_check_range_table(struct regmap *map, unsigned int reg,
+                             const struct regmap_access_table *table);
+
 int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
                          int num_regs);
 
@@ -562,6 +567,13 @@ static inline int regcache_sync_region(struct regmap *map, unsigned int min,
        return -EINVAL;
 }
 
+static inline int regcache_drop_region(struct regmap *map, unsigned int min,
+                                      unsigned int max)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+       return -EINVAL;
+}
+
 static inline void regcache_cache_only(struct regmap *map, bool enable)
 {
        WARN_ONCE(1, "regmap API is disabled");
index a43a2f67bd8e080d844a780a2255dc77af898762..23d561512f64fe65a5165a23d4620f7d457504a4 100644 (file)
@@ -223,6 +223,29 @@ DEFINE_EVENT(regmap_async, regmap_async_complete_done,
 
 );
 
+TRACE_EVENT(regcache_drop_region,
+
+       TP_PROTO(struct device *dev, unsigned int from,
+                unsigned int to),
+
+       TP_ARGS(dev, from, to),
+
+       TP_STRUCT__entry(
+               __string(       name,           dev_name(dev)   )
+               __field(        unsigned int,   from            )
+               __field(        unsigned int,   to              )
+       ),
+
+       TP_fast_assign(
+               __assign_str(name, dev_name(dev));
+               __entry->from = from;
+               __entry->to = to;
+       ),
+
+       TP_printk("%s %u-%u", __get_str(name), (unsigned int)__entry->from,
+                 (unsigned int)__entry->to)
+);
+
 #endif /* _TRACE_REGMAP_H */
 
 /* This part must be outside protection */