Merge tag 'regmap-v3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[cascardo/linux.git] / drivers / base / regmap / regmap.c
index d0c81d1..e0d0c7d 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,9 +121,12 @@ 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;
+       if (map->cache_ops)
+               return false;
+       else
+               return true;
 }
 
 bool regmap_precious(struct regmap *map, unsigned int reg)
@@ -135,7 +138,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 +305,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)
@@ -801,6 +807,95 @@ struct regmap *devm_regmap_init(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(devm_regmap_init);
 
+static void regmap_field_init(struct regmap_field *rm_field,
+       struct regmap *regmap, struct reg_field reg_field)
+{
+       int field_bits = reg_field.msb - reg_field.lsb + 1;
+       rm_field->regmap = regmap;
+       rm_field->reg = reg_field.reg;
+       rm_field->shift = reg_field.lsb;
+       rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb);
+}
+
+/**
+ * devm_regmap_field_alloc(): Allocate and initialise a register field
+ * in a register map.
+ *
+ * @dev: Device that will be interacted with
+ * @regmap: regmap bank in which this register field is located.
+ * @reg_field: Register field with in the bank.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap_field. The regmap_field will be automatically freed
+ * by the device management code.
+ */
+struct regmap_field *devm_regmap_field_alloc(struct device *dev,
+               struct regmap *regmap, struct reg_field reg_field)
+{
+       struct regmap_field *rm_field = devm_kzalloc(dev,
+                                       sizeof(*rm_field), GFP_KERNEL);
+       if (!rm_field)
+               return ERR_PTR(-ENOMEM);
+
+       regmap_field_init(rm_field, regmap, reg_field);
+
+       return rm_field;
+
+}
+EXPORT_SYMBOL_GPL(devm_regmap_field_alloc);
+
+/**
+ * devm_regmap_field_free(): Free register field allocated using
+ * devm_regmap_field_alloc. Usally drivers need not call this function,
+ * as the memory allocated via devm will be freed as per device-driver
+ * life-cyle.
+ *
+ * @dev: Device that will be interacted with
+ * @field: regmap field which should be freed.
+ */
+void devm_regmap_field_free(struct device *dev,
+       struct regmap_field *field)
+{
+       devm_kfree(dev, field);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_field_free);
+
+/**
+ * regmap_field_alloc(): Allocate and initialise a register field
+ * in a register map.
+ *
+ * @regmap: regmap bank in which this register field is located.
+ * @reg_field: Register field with in the bank.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap_field. The regmap_field should be freed by the
+ * user once its finished working with it using regmap_field_free().
+ */
+struct regmap_field *regmap_field_alloc(struct regmap *regmap,
+               struct reg_field reg_field)
+{
+       struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL);
+
+       if (!rm_field)
+               return ERR_PTR(-ENOMEM);
+
+       regmap_field_init(rm_field, regmap, reg_field);
+
+       return rm_field;
+}
+EXPORT_SYMBOL_GPL(regmap_field_alloc);
+
+/**
+ * regmap_field_free(): Free register field allocated using regmap_field_alloc
+ *
+ * @field: regmap field which should be freed.
+ */
+void regmap_field_free(struct regmap_field *field)
+{
+       kfree(field);
+}
+EXPORT_SYMBOL_GPL(regmap_field_free);
+
 /**
  * regmap_reinit_cache(): Reinitialise the current register cache
  *
@@ -1249,6 +1344,22 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
 }
 EXPORT_SYMBOL_GPL(regmap_raw_write);
 
+/**
+ * regmap_field_write(): Write a value to a single register field
+ *
+ * @field: Register field to write to
+ * @val: Value to be written
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_field_write(struct regmap_field *field, unsigned int val)
+{
+       return regmap_update_bits(field->regmap, field->reg,
+                               field->mask, val << field->shift);
+}
+EXPORT_SYMBOL_GPL(regmap_field_write);
+
 /*
  * regmap_bulk_write(): Write multiple registers to the device
  *
@@ -1531,6 +1642,31 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
 }
 EXPORT_SYMBOL_GPL(regmap_raw_read);
 
+/**
+ * regmap_field_read(): Read a value to a single register field
+ *
+ * @field: Register field to read from
+ * @val: Pointer to store read value
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_field_read(struct regmap_field *field, unsigned int *val)
+{
+       int ret;
+       unsigned int reg_val;
+       ret = regmap_read(field->regmap, field->reg, &reg_val);
+       if (ret != 0)
+               return ret;
+
+       reg_val &= field->mask;
+       reg_val >>= field->shift;
+       *val = reg_val;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_field_read);
+
 /**
  * regmap_bulk_read(): Read multiple registers from the device
  *