Merge remote-tracking branch 'regmap/topic/lock' into regmap-next
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Tue, 11 Dec 2012 03:39:30 +0000 (12:39 +0900)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Tue, 11 Dec 2012 03:39:30 +0000 (12:39 +0900)
1  2 
drivers/base/regmap/internal.h
drivers/base/regmap/regmap.c
include/linux/regmap.h

  
  #include <linux/regmap.h>
  #include <linux/fs.h>
 +#include <linux/list.h>
  
  struct regmap;
  struct regcache_ops;
  
 +struct regmap_debugfs_off_cache {
 +      struct list_head list;
 +      off_t min;
 +      off_t max;
 +      unsigned int base_reg;
 +};
 +
  struct regmap_format {
        size_t buf_size;
        size_t reg_bytes;
        unsigned int (*parse_val)(void *buf);
  };
  
- typedef void (*regmap_lock)(struct regmap *map);
- typedef void (*regmap_unlock)(struct regmap *map);
  struct regmap {
        struct mutex mutex;
        spinlock_t spinlock;
        regmap_lock lock;
        regmap_unlock unlock;
+       void *lock_arg; /* This is passed to lock/unlock functions */
  
        struct device *dev; /* Device we do I/O on */
        void *work_buf;     /* Scratch buffer used to format I/O */
  #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs;
        const char *debugfs_name;
 +
 +      unsigned int debugfs_reg_len;
 +      unsigned int debugfs_val_len;
 +      unsigned int debugfs_tot_len;
 +
 +      struct list_head debugfs_off_cache;
  #endif
  
        unsigned int max_register;
@@@ -134,8 -118,6 +132,8 @@@ int _regmap_write(struct regmap *map, u
  
  struct regmap_range_node {
        struct rb_node node;
 +      const char *name;
 +      struct regmap *map;
  
        unsigned int range_min;
        unsigned int range_max;
@@@ -214,23 -214,27 +214,27 @@@ static unsigned int regmap_parse_32_nat
        return *(u32 *)buf;
  }
  
- static void regmap_lock_mutex(struct regmap *map)
+ static void regmap_lock_mutex(void *__map)
  {
+       struct regmap *map = __map;
        mutex_lock(&map->mutex);
  }
  
- static void regmap_unlock_mutex(struct regmap *map)
+ static void regmap_unlock_mutex(void *__map)
  {
+       struct regmap *map = __map;
        mutex_unlock(&map->mutex);
  }
  
- static void regmap_lock_spinlock(struct regmap *map)
+ static void regmap_lock_spinlock(void *__map)
  {
+       struct regmap *map = __map;
        spin_lock(&map->spinlock);
  }
  
- static void regmap_unlock_spinlock(struct regmap *map)
+ static void regmap_unlock_spinlock(void *__map)
  {
+       struct regmap *map = __map;
        spin_unlock(&map->spinlock);
  }
  
@@@ -335,14 -339,21 +339,21 @@@ struct regmap *regmap_init(struct devic
                goto err;
        }
  
-       if (bus->fast_io) {
-               spin_lock_init(&map->spinlock);
-               map->lock = regmap_lock_spinlock;
-               map->unlock = regmap_unlock_spinlock;
+       if (config->lock && config->unlock) {
+               map->lock = config->lock;
+               map->unlock = config->unlock;
+               map->lock_arg = config->lock_arg;
        } else {
-               mutex_init(&map->mutex);
-               map->lock = regmap_lock_mutex;
-               map->unlock = regmap_unlock_mutex;
+               if (bus->fast_io) {
+                       spin_lock_init(&map->spinlock);
+                       map->lock = regmap_lock_spinlock;
+                       map->unlock = regmap_unlock_spinlock;
+               } else {
+                       mutex_init(&map->mutex);
+                       map->lock = regmap_lock_mutex;
+                       map->unlock = regmap_unlock_mutex;
+               }
+               map->lock_arg = map;
        }
        map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
        map->format.pad_bytes = config->pad_bits / 8;
        }
  
        map->range_tree = RB_ROOT;
 -      for (i = 0; i < config->n_ranges; i++) {
 +      for (i = 0; i < config->num_ranges; i++) {
                const struct regmap_range_cfg *range_cfg = &config->ranges[i];
                struct regmap_range_node *new;
  
                /* Sanity check */
 -              if (range_cfg->range_max < range_cfg->range_min ||
 -                  range_cfg->range_max > map->max_register ||
 -                  range_cfg->selector_reg > map->max_register ||
 -                  range_cfg->window_len == 0)
 +              if (range_cfg->range_max < range_cfg->range_min) {
 +                      dev_err(map->dev, "Invalid range %d: %d < %d\n", i,
 +                              range_cfg->range_max, range_cfg->range_min);
                        goto err_range;
 +              }
 +
 +              if (range_cfg->range_max > map->max_register) {
 +                      dev_err(map->dev, "Invalid range %d: %d > %d\n", i,
 +                              range_cfg->range_max, map->max_register);
 +                      goto err_range;
 +              }
 +
 +              if (range_cfg->selector_reg > map->max_register) {
 +                      dev_err(map->dev,
 +                              "Invalid range %d: selector out of map\n", i);
 +                      goto err_range;
 +              }
 +
 +              if (range_cfg->window_len == 0) {
 +                      dev_err(map->dev, "Invalid range %d: window_len 0\n",
 +                              i);
 +                      goto err_range;
 +              }
  
                /* Make sure, that this register range has no selector
                   or data window within its boundary */
 -              for (j = 0; j < config->n_ranges; j++) {
 +              for (j = 0; j < config->num_ranges; j++) {
                        unsigned sel_reg = config->ranges[j].selector_reg;
                        unsigned win_min = config->ranges[j].window_start;
                        unsigned win_max = win_min +
  
                        if (range_cfg->range_min <= sel_reg &&
                            sel_reg <= range_cfg->range_max) {
 +                              dev_err(map->dev,
 +                                      "Range %d: selector for %d in window\n",
 +                                      i, j);
                                goto err_range;
                        }
  
                        if (!(win_max < range_cfg->range_min ||
                              win_min > range_cfg->range_max)) {
 +                              dev_err(map->dev,
 +                                      "Range %d: window for %d in window\n",
 +                                      i, j);
                                goto err_range;
                        }
                }
                        goto err_range;
                }
  
 +              new->map = map;
 +              new->name = range_cfg->name;
                new->range_min = range_cfg->range_min;
                new->range_max = range_cfg->range_max;
                new->selector_reg = range_cfg->selector_reg;
                new->window_len = range_cfg->window_len;
  
                if (_regmap_range_add(map, new) == false) {
 +                      dev_err(map->dev, "Failed to add range %d\n", i);
                        kfree(new);
                        goto err_range;
                }
        }
  
        ret = regcache_init(map, config);
 -      if (ret < 0)
 +      if (ret != 0)
                goto err_range;
  
        regmap_debugfs_init(map, config->name);
@@@ -765,57 -749,59 +776,57 @@@ struct regmap *dev_get_regmap(struct de
  EXPORT_SYMBOL_GPL(dev_get_regmap);
  
  static int _regmap_select_page(struct regmap *map, unsigned int *reg,
 +                             struct regmap_range_node *range,
                               unsigned int val_num)
  {
 -      struct regmap_range_node *range;
        void *orig_work_buf;
        unsigned int win_offset;
        unsigned int win_page;
        bool page_chg;
        int ret;
  
 -      range = _regmap_range_lookup(map, *reg);
 -      if (range) {
 -              win_offset = (*reg - range->range_min) % range->window_len;
 -              win_page = (*reg - range->range_min) / range->window_len;
 -
 -              if (val_num > 1) {
 -                      /* Bulk write shouldn't cross range boundary */
 -                      if (*reg + val_num - 1 > range->range_max)
 -                              return -EINVAL;
 +      win_offset = (*reg - range->range_min) % range->window_len;
 +      win_page = (*reg - range->range_min) / range->window_len;
  
 -                      /* ... or single page boundary */
 -                      if (val_num > range->window_len - win_offset)
 -                              return -EINVAL;
 -              }
 +      if (val_num > 1) {
 +              /* Bulk write shouldn't cross range boundary */
 +              if (*reg + val_num - 1 > range->range_max)
 +                      return -EINVAL;
  
 -              /* It is possible to have selector register inside data window.
 -                 In that case, selector register is located on every page and
 -                 it needs no page switching, when accessed alone. */
 -              if (val_num > 1 ||
 -                  range->window_start + win_offset != range->selector_reg) {
 -                      /* Use separate work_buf during page switching */
 -                      orig_work_buf = map->work_buf;
 -                      map->work_buf = map->selector_work_buf;
 +              /* ... or single page boundary */
 +              if (val_num > range->window_len - win_offset)
 +                      return -EINVAL;
 +      }
  
 -                      ret = _regmap_update_bits(map, range->selector_reg,
 -                                      range->selector_mask,
 -                                      win_page << range->selector_shift,
 -                                      &page_chg);
 +      /* It is possible to have selector register inside data window.
 +         In that case, selector register is located on every page and
 +         it needs no page switching, when accessed alone. */
 +      if (val_num > 1 ||
 +          range->window_start + win_offset != range->selector_reg) {
 +              /* Use separate work_buf during page switching */
 +              orig_work_buf = map->work_buf;
 +              map->work_buf = map->selector_work_buf;
  
 -                      map->work_buf = orig_work_buf;
 +              ret = _regmap_update_bits(map, range->selector_reg,
 +                                        range->selector_mask,
 +                                        win_page << range->selector_shift,
 +                                        &page_chg);
  
 -                      if (ret < 0)
 -                              return ret;
 -              }
 +              map->work_buf = orig_work_buf;
  
 -              *reg = range->window_start + win_offset;
 +              if (ret != 0)
 +                      return ret;
        }
  
 +      *reg = range->window_start + win_offset;
 +
        return 0;
  }
  
  static int _regmap_raw_write(struct regmap *map, unsigned int reg,
                             const void *val, size_t val_len)
  {
 +      struct regmap_range_node *range;
        u8 *u8 = map->work_buf;
        void *buf;
        int ret = -ENOTSUPP;
                                             ival);
                        if (ret) {
                                dev_err(map->dev,
 -                                 "Error in caching of register: %u ret: %d\n",
 +                                      "Error in caching of register: %x ret: %d\n",
                                        reg + i, ret);
                                return ret;
                        }
                }
        }
  
 -      ret = _regmap_select_page(map, &reg, val_len / map->format.val_bytes);
 -      if (ret < 0)
 -              return ret;
 +      range = _regmap_range_lookup(map, reg);
 +      if (range) {
 +              int val_num = val_len / map->format.val_bytes;
 +              int win_offset = (reg - range->range_min) % range->window_len;
 +              int win_residue = range->window_len - win_offset;
 +
 +              /* If the write goes beyond the end of the window split it */
 +              while (val_num > win_residue) {
 +                      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);
 +                      if (ret != 0)
 +                              return ret;
 +
 +                      reg += win_residue;
 +                      val_num -= win_residue;
 +                      val += win_residue * map->format.val_bytes;
 +                      val_len -= win_residue * map->format.val_bytes;
 +
 +                      win_offset = (reg - range->range_min) %
 +                              range->window_len;
 +                      win_residue = range->window_len - win_offset;
 +              }
 +
 +              ret = _regmap_select_page(map, &reg, range, val_num);
 +              if (ret != 0)
 +                      return ret;
 +      }
  
        map->format.format_reg(map->work_buf, reg, map->reg_shift);
  
  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);
  
        trace_regmap_reg_write(map->dev, reg, val);
  
        if (map->format.format_write) {
 -              ret = _regmap_select_page(map, &reg, 1);
 -              if (ret < 0)
 -                      return ret;
 +              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);
  
@@@ -994,11 -950,11 +1005,11 @@@ int regmap_write(struct regmap *map, un
        if (reg % map->reg_stride)
                return -EINVAL;
  
-       map->lock(map);
+       map->lock(map->lock_arg);
  
        ret = _regmap_write(map, reg, val);
  
-       map->unlock(map);
+       map->unlock(map->lock_arg);
  
        return ret;
  }
@@@ -1030,11 -986,11 +1041,11 @@@ int regmap_raw_write(struct regmap *map
        if (reg % map->reg_stride)
                return -EINVAL;
  
-       map->lock(map);
+       map->lock(map->lock_arg);
  
        ret = _regmap_raw_write(map, reg, val, val_len);
  
-       map->unlock(map);
+       map->unlock(map->lock_arg);
  
        return ret;
  }
@@@ -1066,7 -1022,7 +1077,7 @@@ int regmap_bulk_write(struct regmap *ma
        if (reg % map->reg_stride)
                return -EINVAL;
  
-       map->lock(map);
+       map->lock(map->lock_arg);
  
        /* No formatting is require if val_byte is 1 */
        if (val_bytes == 1) {
                kfree(wval);
  
  out:
-       map->unlock(map);
+       map->unlock(map->lock_arg);
        return ret;
  }
  EXPORT_SYMBOL_GPL(regmap_bulk_write);
  static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
                            unsigned int val_len)
  {
 +      struct regmap_range_node *range;
        u8 *u8 = map->work_buf;
        int ret;
  
 -      ret = _regmap_select_page(map, &reg, val_len / map->format.val_bytes);
 -      if (ret < 0)
 -              return ret;
 +      range = _regmap_range_lookup(map, reg);
 +      if (range) {
 +              ret = _regmap_select_page(map, &reg, range,
 +                                        val_len / map->format.val_bytes);
 +              if (ret != 0)
 +                      return ret;
 +      }
  
        map->format.format_reg(map->work_buf, reg, map->reg_shift);
  
@@@ -1197,11 -1148,11 +1208,11 @@@ int regmap_read(struct regmap *map, uns
        if (reg % map->reg_stride)
                return -EINVAL;
  
-       map->lock(map);
+       map->lock(map->lock_arg);
  
        ret = _regmap_read(map, reg, val);
  
-       map->unlock(map);
+       map->unlock(map->lock_arg);
  
        return ret;
  }
@@@ -1231,7 -1182,7 +1242,7 @@@ int regmap_raw_read(struct regmap *map
        if (reg % map->reg_stride)
                return -EINVAL;
  
-       map->lock(map);
+       map->lock(map->lock_arg);
  
        if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass ||
            map->cache_type == REGCACHE_NONE) {
        }
  
   out:
-       map->unlock(map);
+       map->unlock(map->lock_arg);
  
        return ret;
  }
@@@ -1360,9 -1311,9 +1371,9 @@@ int regmap_update_bits(struct regmap *m
        bool change;
        int ret;
  
-       map->lock(map);
+       map->lock(map->lock_arg);
        ret = _regmap_update_bits(map, reg, mask, val, &change);
-       map->unlock(map);
+       map->unlock(map->lock_arg);
  
        return ret;
  }
@@@ -1386,9 -1337,9 +1397,9 @@@ int regmap_update_bits_check(struct reg
  {
        int ret;
  
-       map->lock(map);
+       map->lock(map->lock_arg);
        ret = _regmap_update_bits(map, reg, mask, val, change);
-       map->unlock(map);
+       map->unlock(map->lock_arg);
        return ret;
  }
  EXPORT_SYMBOL_GPL(regmap_update_bits_check);
@@@ -1417,7 -1368,7 +1428,7 @@@ int regmap_register_patch(struct regma
        if (map->patch)
                return -EBUSY;
  
-       map->lock(map);
+       map->lock(map->lock_arg);
  
        bypass = map->cache_bypass;
  
  out:
        map->cache_bypass = bypass;
  
-       map->unlock(map);
+       map->unlock(map->lock_arg);
  
        return ret;
  }
diff --combined include/linux/regmap.h
@@@ -19,7 -19,6 +19,7 @@@
  struct module;
  struct device;
  struct i2c_client;
 +struct irq_domain;
  struct spi_device;
  struct regmap;
  struct regmap_range_cfg;
@@@ -54,6 -53,9 +54,9 @@@ enum regmap_endian 
        REGMAP_ENDIAN_NATIVE,
  };
  
+ typedef void (*regmap_lock)(void *);
+ typedef void (*regmap_unlock)(void *);
  /**
   * Configuration for the register map of a device.
   *
   * @precious_reg: Optional callback returning true if the rgister
   *                should not be read outside of a call from the driver
   *                (eg, a clear on read interrupt status register).
+  * @lock:         Optional lock callback (overrides regmap's default lock
+  *                function, based on spinlock or mutex).
+  * @unlock:       As above for unlocking.
+  * @lock_arg:     this field is passed as the only argument of lock/unlock
+  *                functions (ignored in case regular lock/unlock functions
+  *                are not overridden).
   *
   * @max_register: Optional, specifies the maximum valid register index.
   * @reg_defaults: Power on reset values for registers (for use with
@@@ -117,6 -125,9 +126,9 @@@ struct regmap_config 
        bool (*readable_reg)(struct device *dev, unsigned int reg);
        bool (*volatile_reg)(struct device *dev, unsigned int reg);
        bool (*precious_reg)(struct device *dev, unsigned int reg);
+       regmap_lock lock;
+       regmap_unlock unlock;
+       void *lock_arg;
  
        unsigned int max_register;
        const struct reg_default *reg_defaults;
        enum regmap_endian val_format_endian;
  
        const struct regmap_range_cfg *ranges;
 -      unsigned int n_ranges;
 +      unsigned int num_ranges;
  };
  
  /**
   *     1. page selector register update;
   *     2. access through data window registers.
   *
 + * @name: Descriptive name for diagnostics
 + *
   * @range_min: Address of the lowest register address in virtual range.
   * @range_max: Address of the highest register in virtual range.
   *
   * @window_len: Number of registers in data window.
   */
  struct regmap_range_cfg {
 +      const char *name;
 +
        /* Registers of virtual address range */
        unsigned int range_min;
        unsigned int range_max;
@@@ -186,7 -193,9 +198,9 @@@ typedef void (*regmap_hw_free_context)(
   * Description of a hardware bus for the register map infrastructure.
   *
   * @fast_io: Register IO is fast. Use a spinlock instead of a mutex
-  *           to perform locking.
+  *         to perform locking. This field is ignored if custom lock/unlock
+  *         functions are used (see fields lock/unlock of
+  *         struct regmap_config).
   * @write: Write operation.
   * @gather_write: Write operation with split register/value, return -ENOTSUPP
   *                if not implemented  on a given device.
@@@ -322,7 -331,6 +336,7 @@@ int regmap_add_irq_chip(struct regmap *
  void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data);
  int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data);
  int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq);
 +struct irq_domain *regmap_irq_get_domain(struct regmap_irq_chip_data *data);
  
  #else