Merge branch 'regmap-linus' into regmap-next
[pandora-kernel.git] / drivers / base / regmap / regmap.c
index 20663f8..86b1847 100644 (file)
 #include <linux/mutex.h>
 #include <linux/err.h>
 
-#include <linux/regmap.h>
-
-struct regmap;
-
-struct regmap_format {
-       size_t buf_size;
-       size_t reg_bytes;
-       size_t val_bytes;
-       void (*format_write)(struct regmap *map,
-                            unsigned int reg, unsigned int val);
-       void (*format_reg)(void *buf, unsigned int reg);
-       void (*format_val)(void *buf, unsigned int val);
-       unsigned int (*parse_val)(void *buf);
-};
-
-struct regmap {
-       struct mutex lock;
-
-       struct device *dev; /* Device we do I/O on */
-       void *work_buf;     /* Scratch buffer used to format I/O */
-       struct regmap_format format;  /* Buffer format */
-       const struct regmap_bus *bus;
-};
+#define CREATE_TRACE_POINTS
+#include <trace/events/regmap.h>
+
+#include "internal.h"
+
+bool regmap_writeable(struct regmap *map, unsigned int reg)
+{
+       if (map->max_register && reg > map->max_register)
+               return false;
+
+       if (map->writeable_reg)
+               return map->writeable_reg(map->dev, reg);
+
+       return true;
+}
+
+bool regmap_readable(struct regmap *map, unsigned int reg)
+{
+       if (map->max_register && reg > map->max_register)
+               return false;
+
+       if (map->readable_reg)
+               return map->readable_reg(map->dev, reg);
+
+       return true;
+}
+
+bool regmap_volatile(struct regmap *map, unsigned int reg)
+{
+       if (map->max_register && reg > map->max_register)
+               return false;
+
+       if (map->volatile_reg)
+               return map->volatile_reg(map->dev, reg);
+
+       return true;
+}
+
+bool regmap_precious(struct regmap *map, unsigned int reg)
+{
+       if (map->max_register && reg > map->max_register)
+               return false;
+
+       if (map->precious_reg)
+               return map->precious_reg(map->dev, reg);
+
+       return false;
+}
 
 static void regmap_format_4_12_write(struct regmap *map,
                                     unsigned int reg, unsigned int val)
@@ -116,6 +141,11 @@ struct regmap *regmap_init(struct device *dev,
        map->format.val_bytes = config->val_bits / 8;
        map->dev = dev;
        map->bus = bus;
+       map->max_register = config->max_register;
+       map->writeable_reg = config->writeable_reg;
+       map->readable_reg = config->readable_reg;
+       map->volatile_reg = config->volatile_reg;
+       map->precious_reg = config->precious_reg;
 
        switch (config->reg_bits) {
        case 4:
@@ -171,6 +201,8 @@ struct regmap *regmap_init(struct device *dev,
                goto err_map;
        }
 
+       regmap_debugfs_init(map);
+
        return map;
 
 err_map:
@@ -185,6 +217,7 @@ EXPORT_SYMBOL_GPL(regmap_init);
  */
 void regmap_exit(struct regmap *map)
 {
+       regmap_debugfs_exit(map);
        kfree(map->work_buf);
        kfree(map);
 }
@@ -196,16 +229,32 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
        void *buf;
        int ret = -ENOTSUPP;
        size_t len;
+       int i;
+
+       /* Check for unwritable registers before we start */
+       if (map->writeable_reg)
+               for (i = 0; i < val_len / map->format.val_bytes; i++)
+                       if (!map->writeable_reg(map->dev, reg + i))
+                               return -EINVAL;
 
        map->format.format_reg(map->work_buf, reg);
 
-       /* Try to do a gather write if we can */
-       if (map->bus->gather_write)
+       trace_regmap_hw_write_start(map->dev, reg,
+                                   val_len / map->format.val_bytes);
+
+       /* If we're doing a single register write we can probably just
+        * send the work_buf directly, otherwise try to do a gather
+        * write.
+        */
+       if (val == map->work_buf + map->format.reg_bytes)
+               ret = map->bus->write(map->dev, map->work_buf,
+                                     map->format.reg_bytes + val_len);
+       else if (map->bus->gather_write)
                ret = map->bus->gather_write(map->dev, map->work_buf,
                                             map->format.reg_bytes,
                                             val, val_len);
 
-       /* Otherwise fall back on linearising by hand. */
+       /* If that didn't work fall back on linearising by hand. */
        if (ret == -ENOTSUPP) {
                len = map->format.reg_bytes + val_len;
                buf = kmalloc(len, GFP_KERNEL);
@@ -219,19 +268,31 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
                kfree(buf);
        }
 
+       trace_regmap_hw_write_done(map->dev, reg,
+                                  val_len / map->format.val_bytes);
+
        return ret;
 }
 
 static int _regmap_write(struct regmap *map, unsigned int reg,
                         unsigned int val)
 {
+       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) {
                map->format.format_write(map, reg, val);
 
-               return map->bus->write(map->dev, map->work_buf,
-                                      map->format.buf_size);
+               trace_regmap_hw_write_start(map->dev, reg, 1);
+
+               ret = map->bus->write(map->dev, 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,
                                       val);
@@ -313,12 +374,16 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
        if (map->bus->read_flag_mask)
                u8[0] |= map->bus->read_flag_mask;
 
+       trace_regmap_hw_read_start(map->dev, reg,
+                                  val_len / map->format.val_bytes);
+
        ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes,
                             val, val_len);
-       if (ret != 0)
-               return ret;
 
-       return 0;
+       trace_regmap_hw_read_done(map->dev, reg,
+                                 val_len / map->format.val_bytes);
+
+       return ret;
 }
 
 static int _regmap_read(struct regmap *map, unsigned int reg,
@@ -330,8 +395,10 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
                return -EINVAL;
 
        ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
-       if (ret == 0)
+       if (ret == 0) {
                *val = map->format.parse_val(map->work_buf);
+               trace_regmap_reg_read(map->dev, reg, *val);
+       }
 
        return ret;
 }
@@ -450,3 +517,11 @@ out:
        return ret;
 }
 EXPORT_SYMBOL_GPL(regmap_update_bits);
+
+static int __init regmap_initcall(void)
+{
+       regmap_debugfs_initcall();
+
+       return 0;
+}
+postcore_initcall(regmap_initcall);