From ea279fc5619e2541a0c28196b0fa06447d9ad026 Mon Sep 17 00:00:00 2001 From: Marc Reilly Date: Fri, 16 Mar 2012 12:11:42 +1100 Subject: [PATCH 01/10] regmap: Add support for device with 24 data bits. Add support for devices with 24 data bits. Signed-off-by: Marc Reilly Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7a3f535e481c..8ffce9bdb481 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -126,6 +126,15 @@ static void regmap_format_16(void *buf, unsigned int val) b[0] = cpu_to_be16(val); } +static void regmap_format_24(void *buf, unsigned int val) +{ + u8 *b = buf; + + b[0] = val >> 16; + b[1] = val >> 8; + b[2] = val; +} + static void regmap_format_32(void *buf, unsigned int val) { __be32 *b = buf; @@ -149,6 +158,16 @@ static unsigned int regmap_parse_16(void *buf) return b[0]; } +static unsigned int regmap_parse_24(void *buf) +{ + u8 *b = buf; + unsigned int ret = b[2]; + ret |= ((unsigned int)b[1]) << 8; + ret |= ((unsigned int)b[0]) << 16; + + return ret; +} + static unsigned int regmap_parse_32(void *buf) { __be32 *b = buf; @@ -273,6 +292,10 @@ struct regmap *regmap_init(struct device *dev, map->format.format_val = regmap_format_16; map->format.parse_val = regmap_parse_16; break; + case 24: + map->format.format_val = regmap_format_24; + map->format.parse_val = regmap_parse_24; + break; case 32: map->format.format_val = regmap_format_32; map->format.parse_val = regmap_parse_32; From d939fb9a78b4743bc4bc3cc415894ed42050c5cc Mon Sep 17 00:00:00 2001 From: Marc Reilly Date: Fri, 16 Mar 2012 12:11:43 +1100 Subject: [PATCH 02/10] regmap: Use pad_bits and reg_bits when determining register format. This change combines any padding bits into the register address bits when determining register format handlers to use the next byte-divisible register size. A reg_shift member is introduced to the regmap struct to enable fixup of the reg format. Format handlers now take an extra parameter specifying the number of bits to shift the value by. Signed-off-by: Marc Reilly Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 7 +++++-- drivers/base/regmap/regmap.c | 27 +++++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index fcafc5b2e651..606b83d75458 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -26,8 +26,8 @@ struct regmap_format { 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); + void (*format_reg)(void *buf, unsigned int reg, unsigned int shift); + void (*format_val)(void *buf, unsigned int val, unsigned int shift); unsigned int (*parse_val)(void *buf); }; @@ -52,6 +52,9 @@ struct regmap { u8 read_flag_mask; u8 write_flag_mask; + /* number of bits to (left) shift the reg value when formatting*/ + int reg_shift; + /* regcache specific members */ const struct regcache_ops *cache_ops; enum regcache_type cache_type; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 8ffce9bdb481..178989a8949e 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -112,34 +112,36 @@ static void regmap_format_10_14_write(struct regmap *map, out[0] = reg >> 2; } -static void regmap_format_8(void *buf, unsigned int val) +static void regmap_format_8(void *buf, unsigned int val, unsigned int shift) { u8 *b = buf; - b[0] = val; + b[0] = val << shift; } -static void regmap_format_16(void *buf, unsigned int val) +static void regmap_format_16(void *buf, unsigned int val, unsigned int shift) { __be16 *b = buf; - b[0] = cpu_to_be16(val); + b[0] = cpu_to_be16(val << shift); } -static void regmap_format_24(void *buf, unsigned int val) +static void regmap_format_24(void *buf, unsigned int val, unsigned int shift) { u8 *b = buf; + val <<= shift; + b[0] = val >> 16; b[1] = val >> 8; b[2] = val; } -static void regmap_format_32(void *buf, unsigned int val) +static void regmap_format_32(void *buf, unsigned int val, unsigned int shift) { __be32 *b = buf; - b[0] = cpu_to_be32(val); + b[0] = cpu_to_be32(val << shift); } static unsigned int regmap_parse_8(void *buf) @@ -210,6 +212,7 @@ struct regmap *regmap_init(struct device *dev, map->format.pad_bytes = config->pad_bits / 8; map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); map->format.buf_size += map->format.pad_bytes; + map->reg_shift = config->pad_bits % 8; map->dev = dev; map->bus = bus; map->max_register = config->max_register; @@ -226,7 +229,7 @@ struct regmap *regmap_init(struct device *dev, map->read_flag_mask = bus->read_flag_mask; } - switch (config->reg_bits) { + switch (config->reg_bits + map->reg_shift) { case 2: switch (config->val_bits) { case 6: @@ -454,7 +457,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, } } - map->format.format_reg(map->work_buf, reg); + map->format.format_reg(map->work_buf, reg, map->reg_shift); u8[0] |= map->write_flag_mask; @@ -529,7 +532,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, return ret; } else { map->format.format_val(map->work_buf + map->format.reg_bytes - + map->format.pad_bytes, val); + + map->format.pad_bytes, val, 0); return _regmap_raw_write(map, reg, map->work_buf + map->format.reg_bytes + @@ -649,7 +652,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, u8 *u8 = map->work_buf; int ret; - map->format.format_reg(map->work_buf, reg); + map->format.format_reg(map->work_buf, reg, map->reg_shift); /* * Some buses or devices flag reads by setting the high bits in the @@ -757,7 +760,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, if (ret != 0) goto out; - map->format.format_val(val + (i * val_bytes), v); + map->format.format_val(val + (i * val_bytes), v, 0); } } From 0135bbcc7a0cc056f0203ff839466236b8e3dc19 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:30 -0600 Subject: [PATCH 03/10] regmap: introduce explicit bus_context for bus callbacks The only context needed by I2C and SPI bus definitions is the device itself; this can be converted to an i2c_client or spi_device in order to perform IO on the device. However, other bus types may need more context in order to perform IO. Enable this by having regmap_init accept a bus_context parameter, and pass this to all bus callbacks. The existing callbacks simply pass the struct device here. Future bus types may pass something else. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap-i2c.c | 13 ++++++++----- drivers/base/regmap/regmap-spi.c | 13 ++++++++----- drivers/base/regmap/regmap.c | 19 +++++++++++++------ include/linux/regmap.h | 10 +++++++--- 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index fcafc5b2e651..b95fd1f25295 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -38,6 +38,7 @@ struct regmap { void *work_buf; /* Scratch buffer used to format I/O */ struct regmap_format format; /* Buffer format */ const struct regmap_bus *bus; + void *bus_context; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 9a3a8c564389..5f6b2478bf17 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -15,8 +15,9 @@ #include #include -static int regmap_i2c_write(struct device *dev, const void *data, size_t count) +static int regmap_i2c_write(void *context, const void *data, size_t count) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); int ret; @@ -29,10 +30,11 @@ static int regmap_i2c_write(struct device *dev, const void *data, size_t count) return -EIO; } -static int regmap_i2c_gather_write(struct device *dev, +static int regmap_i2c_gather_write(void *context, const void *reg, size_t reg_size, const void *val, size_t val_size) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); struct i2c_msg xfer[2]; int ret; @@ -62,10 +64,11 @@ static int regmap_i2c_gather_write(struct device *dev, return -EIO; } -static int regmap_i2c_read(struct device *dev, +static int regmap_i2c_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); struct i2c_msg xfer[2]; int ret; @@ -107,7 +110,7 @@ static struct regmap_bus regmap_i2c = { struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config) { - return regmap_init(&i2c->dev, ®map_i2c, config); + return regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config); } EXPORT_SYMBOL_GPL(regmap_init_i2c); @@ -124,7 +127,7 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c); struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config) { - return devm_regmap_init(&i2c->dev, ®map_i2c, config); + return devm_regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config); } EXPORT_SYMBOL_GPL(devm_regmap_init_i2c); diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 7c0c35a39c33..ffa46a92ad33 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -15,17 +15,19 @@ #include #include -static int regmap_spi_write(struct device *dev, const void *data, size_t count) +static int regmap_spi_write(void *context, const void *data, size_t count) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); return spi_write(spi, data, count); } -static int regmap_spi_gather_write(struct device *dev, +static int regmap_spi_gather_write(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); struct spi_message m; struct spi_transfer t[2] = { { .tx_buf = reg, .len = reg_len, }, @@ -38,10 +40,11 @@ static int regmap_spi_gather_write(struct device *dev, return spi_sync(spi, &m); } -static int regmap_spi_read(struct device *dev, +static int regmap_spi_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); return spi_write_then_read(spi, reg, reg_size, val, val_size); @@ -66,7 +69,7 @@ static struct regmap_bus regmap_spi = { struct regmap *regmap_init_spi(struct spi_device *spi, const struct regmap_config *config) { - return regmap_init(&spi->dev, ®map_spi, config); + return regmap_init(&spi->dev, ®map_spi, &spi->dev, config); } EXPORT_SYMBOL_GPL(regmap_init_spi); @@ -83,7 +86,7 @@ EXPORT_SYMBOL_GPL(regmap_init_spi); struct regmap *devm_regmap_init_spi(struct spi_device *spi, const struct regmap_config *config) { - return devm_regmap_init(&spi->dev, ®map_spi, config); + return devm_regmap_init(&spi->dev, ®map_spi, &spi->dev, config); } EXPORT_SYMBOL_GPL(devm_regmap_init_spi); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7a3f535e481c..bde30c5d5ee9 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -163,6 +163,7 @@ static unsigned int regmap_parse_32(void *buf) * * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device + * @bus_context: Data passed to bus-specific callbacks * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer to @@ -171,6 +172,7 @@ static unsigned int regmap_parse_32(void *buf) */ struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config) { struct regmap *map; @@ -193,6 +195,7 @@ struct regmap *regmap_init(struct device *dev, map->format.buf_size += map->format.pad_bytes; map->dev = dev; map->bus = bus; + map->bus_context = bus_context; map->max_register = config->max_register; map->writeable_reg = config->writeable_reg; map->readable_reg = config->readable_reg; @@ -316,6 +319,7 @@ static void devm_regmap_release(struct device *dev, void *res) * * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device + * @bus_context: Data passed to bus-specific callbacks * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer @@ -325,6 +329,7 @@ static void devm_regmap_release(struct device *dev, void *res) */ struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config) { struct regmap **ptr, *regmap; @@ -333,7 +338,7 @@ struct regmap *devm_regmap_init(struct device *dev, if (!ptr) return ERR_PTR(-ENOMEM); - regmap = regmap_init(dev, bus, config); + regmap = regmap_init(dev, bus, bus_context, config); if (!IS_ERR(regmap)) { *ptr = regmap; devres_add(dev, ptr); @@ -391,6 +396,8 @@ void regmap_exit(struct regmap *map) { regcache_exit(map); regmap_debugfs_exit(map); + if (map->bus->free_context) + map->bus->free_context(map->bus_context); kfree(map->work_buf); kfree(map); } @@ -444,12 +451,12 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, */ if (val == (map->work_buf + map->format.pad_bytes + map->format.reg_bytes)) - ret = map->bus->write(map->dev, map->work_buf, + ret = map->bus->write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes + val_len); else if (map->bus->gather_write) - ret = map->bus->gather_write(map->dev, map->work_buf, + ret = map->bus->gather_write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); @@ -464,7 +471,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, memcpy(buf, map->work_buf, map->format.reg_bytes); memcpy(buf + map->format.reg_bytes + map->format.pad_bytes, val, val_len); - ret = map->bus->write(map->dev, buf, len); + ret = map->bus->write(map->bus_context, buf, len); kfree(buf); } @@ -498,7 +505,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_hw_write_start(map->dev, reg, 1); - ret = map->bus->write(map->dev, map->work_buf, + ret = map->bus->write(map->bus_context, map->work_buf, map->format.buf_size); trace_regmap_hw_write_done(map->dev, reg, 1); @@ -639,7 +646,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, trace_regmap_hw_read_start(map->dev, reg, val_len / map->format.val_bytes); - ret = map->bus->read(map->dev, map->work_buf, + ret = map->bus->read(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a90abb6bfa64..8fd341e613d6 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -97,14 +97,15 @@ struct regmap_config { u8 write_flag_mask; }; -typedef int (*regmap_hw_write)(struct device *dev, const void *data, +typedef int (*regmap_hw_write)(void *context, const void *data, size_t count); -typedef int (*regmap_hw_gather_write)(struct device *dev, +typedef int (*regmap_hw_gather_write)(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len); -typedef int (*regmap_hw_read)(struct device *dev, +typedef int (*regmap_hw_read)(void *context, const void *reg_buf, size_t reg_size, void *val_buf, size_t val_size); +typedef void (*regmap_hw_free_context)(void *context); /** * Description of a hardware bus for the register map infrastructure. @@ -121,11 +122,13 @@ struct regmap_bus { regmap_hw_write write; regmap_hw_gather_write gather_write; regmap_hw_read read; + regmap_hw_free_context free_context; u8 read_flag_mask; }; struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config); struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); @@ -134,6 +137,7 @@ struct regmap *regmap_init_spi(struct spi_device *dev, struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config); struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); From bacdbe077342ecc9e7b3e374cc5a41995116706a Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:28 -0600 Subject: [PATCH 04/10] regmap: introduce fast_io busses, and use a spinlock for them Some bus types have very fast IO. For these, acquiring a mutex for every IO operation is a significant overhead. Allow busses to indicate their IO is fast, and enhance regmap to use a spinlock for those busses. [Currently limited to native endian registers -- broonie] Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 8 +++- drivers/base/regmap/regcache-rbtree.c | 4 +- drivers/base/regmap/regcache.c | 20 ++++----- drivers/base/regmap/regmap.c | 62 +++++++++++++++++++-------- include/linux/regmap.h | 3 ++ 5 files changed, 67 insertions(+), 30 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index b95fd1f25295..e46c279f8280 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -31,8 +31,14 @@ struct regmap_format { unsigned int (*parse_val)(void *buf); }; +typedef void (*regmap_lock)(struct regmap *map); +typedef void (*regmap_unlock)(struct regmap *map); + struct regmap { - struct mutex lock; + struct mutex mutex; + spinlock_t spinlock; + regmap_lock lock; + regmap_unlock unlock; struct device *dev; /* Device we do I/O on */ void *work_buf; /* Scratch buffer used to format I/O */ diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 5157fa04c2f0..cca46007d969 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -139,7 +139,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) int nodes = 0; int registers = 0; - mutex_lock(&map->lock); + map->lock(map); for (node = rb_first(&rbtree_ctx->root); node != NULL; node = rb_next(node)) { @@ -155,7 +155,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) seq_printf(s, "%d nodes, %d registers, average %d registers\n", nodes, registers, registers / nodes); - mutex_unlock(&map->lock); + map->unlock(map); return 0; } diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 87f54dbf601b..4ad18505e9ae 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -264,7 +264,7 @@ int regcache_sync(struct regmap *map) BUG_ON(!map->cache_ops || !map->cache_ops->sync); - mutex_lock(&map->lock); + map->lock(map); /* Remember the initial bypass state */ bypass = map->cache_bypass; dev_dbg(map->dev, "Syncing %s cache\n", @@ -296,7 +296,7 @@ int regcache_sync(struct regmap *map) trace_regcache_sync(map->dev, name, "stop"); /* Restore the bypass state */ map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -323,7 +323,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, BUG_ON(!map->cache_ops || !map->cache_ops->sync); - mutex_lock(&map->lock); + map->lock(map); /* Remember the initial bypass state */ bypass = map->cache_bypass; @@ -342,7 +342,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, trace_regcache_sync(map->dev, name, "stop region"); /* Restore the bypass state */ map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -361,11 +361,11 @@ int regcache_sync_region(struct regmap *map, unsigned int min, */ void regcache_cache_only(struct regmap *map, bool enable) { - mutex_lock(&map->lock); + map->lock(map); WARN_ON(map->cache_bypass && enable); map->cache_only = enable; trace_regmap_cache_only(map->dev, enable); - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_cache_only); @@ -380,9 +380,9 @@ EXPORT_SYMBOL_GPL(regcache_cache_only); */ void regcache_mark_dirty(struct regmap *map) { - mutex_lock(&map->lock); + map->lock(map); map->cache_dirty = true; - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_mark_dirty); @@ -399,11 +399,11 @@ EXPORT_SYMBOL_GPL(regcache_mark_dirty); */ void regcache_cache_bypass(struct regmap *map, bool enable) { - mutex_lock(&map->lock); + map->lock(map); WARN_ON(map->cache_only && enable); map->cache_bypass = enable; trace_regmap_cache_bypass(map->dev, enable); - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_cache_bypass); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index bde30c5d5ee9..6b4a775b439b 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -158,6 +158,26 @@ static unsigned int regmap_parse_32(void *buf) return b[0]; } +static void regmap_lock_mutex(struct regmap *map) +{ + mutex_lock(&map->mutex); +} + +static void regmap_unlock_mutex(struct regmap *map) +{ + mutex_unlock(&map->mutex); +} + +static void regmap_lock_spinlock(struct regmap *map) +{ + spin_lock(&map->spinlock); +} + +static void regmap_unlock_spinlock(struct regmap *map) +{ + spin_unlock(&map->spinlock); +} + /** * regmap_init(): Initialise register map * @@ -187,7 +207,15 @@ struct regmap *regmap_init(struct device *dev, goto err; } - mutex_init(&map->lock); + 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->format.buf_size = (config->reg_bits + config->val_bits) / 8; map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.pad_bytes = config->pad_bits / 8; @@ -365,7 +393,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) { int ret; - mutex_lock(&map->lock); + map->lock(map); regcache_exit(map); regmap_debugfs_exit(map); @@ -384,7 +412,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) ret = regcache_init(map, config); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -536,11 +564,11 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_write(map, reg, val); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -567,11 +595,11 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, { int ret; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_raw_write(map, reg, val, val_len); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -601,7 +629,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (!map->format.parse_val) return -EINVAL; - mutex_lock(&map->lock); + map->lock(map); /* No formatting is require if val_byte is 1 */ if (val_bytes == 1) { @@ -622,7 +650,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, kfree(wval); out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_write); @@ -696,11 +724,11 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_read(map, reg, val); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -725,7 +753,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int v; int ret, i; - mutex_lock(&map->lock); + map->lock(map); if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || map->cache_type == REGCACHE_NONE) { @@ -746,7 +774,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, } out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -799,7 +827,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, int ret; unsigned int tmp, orig; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_read(map, reg, &orig); if (ret != 0) @@ -816,7 +844,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, } out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -883,7 +911,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, if (map->patch) return -EBUSY; - mutex_lock(&map->lock); + map->lock(map); bypass = map->cache_bypass; @@ -911,7 +939,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, out: map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 8fd341e613d6..f14588a96eaf 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -110,6 +110,8 @@ typedef void (*regmap_hw_free_context)(void *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. * @write: Write operation. * @gather_write: Write operation with split register/value, return -ENOTSUPP * if not implemented on a given device. @@ -119,6 +121,7 @@ typedef void (*regmap_hw_free_context)(void *context); * a read. */ struct regmap_bus { + bool fast_io; regmap_hw_write write; regmap_hw_gather_write gather_write; regmap_hw_read read; From 45f5ff8107a845854b1d1812ab1d9c5541f08b4d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:31 -0600 Subject: [PATCH 05/10] regmap: add MMIO bus support This is a basic memory-mapped-IO bus for regmap. It has the following features and limitations: * Registers themselves may be 8, 16, 32, or 64-bit. 64-bit is only supported on 64-bit platforms. * Register offsets are limited to precisely 32-bit. * IO is performed using readl/writel, with no provision for using the __raw_readl or readl_relaxed variants. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/Kconfig | 3 + drivers/base/regmap/Makefile | 1 + drivers/base/regmap/regmap-mmio.c | 217 ++++++++++++++++++++++++++++++ include/linux/regmap.h | 6 + 4 files changed, 227 insertions(+) create mode 100644 drivers/base/regmap/regmap-mmio.c diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 0f6c7fb418e8..9ef0a5326f17 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -14,5 +14,8 @@ config REGMAP_I2C config REGMAP_SPI tristate +config REGMAP_MMIO + tristate + config REGMAP_IRQ bool diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index defd57963c84..5e75d1b683e2 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -3,4 +3,5 @@ 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_MMIO) += regmap-mmio.o obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c new file mode 100644 index 000000000000..1a7b5ee11abc --- /dev/null +++ b/drivers/base/regmap/regmap-mmio.c @@ -0,0 +1,217 @@ +/* + * Register map access API - MMIO support + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +struct regmap_mmio_context { + void __iomem *regs; + unsigned val_bytes; +}; + +static int regmap_mmio_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct regmap_mmio_context *ctx = context; + u32 offset; + + if (reg_size != 4) + return -EIO; + if (val_size % ctx->val_bytes) + return -EIO; + + offset = be32_to_cpup(reg); + + while (val_size) { + switch (ctx->val_bytes) { + case 1: + writeb(*(u8 *)val, ctx->regs + offset); + break; + case 2: + writew(be16_to_cpup(val), ctx->regs + offset); + break; + case 4: + writel(be32_to_cpup(val), ctx->regs + offset); + break; +#ifdef CONFIG_64BIT + case 8: + writeq(be64_to_cpup(val), ctx->regs + offset); + break; +#endif + default: + /* Should be caught by regmap_mmio_check_config */ + return -EIO; + } + val_size -= ctx->val_bytes; + val += ctx->val_bytes; + offset += ctx->val_bytes; + } + + return 0; +} + +static int regmap_mmio_write(void *context, const void *data, size_t count) +{ + if (count < 4) + return -EIO; + return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4); +} + +static int regmap_mmio_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct regmap_mmio_context *ctx = context; + u32 offset; + + if (reg_size != 4) + return -EIO; + if (val_size % ctx->val_bytes) + return -EIO; + + offset = be32_to_cpup(reg); + + while (val_size) { + switch (ctx->val_bytes) { + case 1: + *(u8 *)val = readb(ctx->regs + offset); + break; + case 2: + *(u16 *)val = cpu_to_be16(readw(ctx->regs + offset)); + break; + case 4: + *(u32 *)val = cpu_to_be32(readl(ctx->regs + offset)); + break; +#ifdef CONFIG_64BIT + case 8: + *(u64 *)val = cpu_to_be32(readq(ctx->regs + offset)); + break; +#endif + default: + /* Should be caught by regmap_mmio_check_config */ + return -EIO; + } + val_size -= ctx->val_bytes; + val += ctx->val_bytes; + offset += ctx->val_bytes; + } + + return 0; +} + +static void regmap_mmio_free_context(void *context) +{ + kfree(context); +} + +static struct regmap_bus regmap_mmio = { + .fast_io = true, + .write = regmap_mmio_write, + .gather_write = regmap_mmio_gather_write, + .read = regmap_mmio_read, + .free_context = regmap_mmio_free_context, +}; + +struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + if (config->reg_bits != 32) + return ERR_PTR(-EINVAL); + + if (config->pad_bits) + return ERR_PTR(-EINVAL); + + switch (config->val_bits) { + case 8: + case 16: + case 32: +#ifdef CONFIG_64BIT + case 64: +#endif + break; + default: + return ERR_PTR(-EINVAL); + } + + ctx = kzalloc(GFP_KERNEL, sizeof(*ctx)); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->regs = regs; + ctx->val_bytes = config->val_bits / 8; + + return ctx; +} + +/** + * regmap_init_mmio(): Initialise register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +struct regmap *regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + ctx = regmap_mmio_gen_context(regs, config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return regmap_init(dev, ®map_mmio, ctx, config); +} +EXPORT_SYMBOL_GPL(regmap_init_mmio); + +/** + * devm_regmap_init_mmio(): Initialise managed register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + ctx = regmap_mmio_gen_context(regs, config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return devm_regmap_init(dev, ®map_mmio, ctx, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_mmio); + +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index f14588a96eaf..f6abc8d33d64 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -137,6 +137,9 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config); struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, @@ -146,6 +149,9 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *devm_regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *devm_regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config); void regmap_exit(struct regmap *map); int regmap_reinit_cache(struct regmap *map, From 40606dba450830e50420599c52a86cf6ce5c6a14 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 6 Apr 2012 15:17:32 -0600 Subject: [PATCH 06/10] regmap: mmio: convert some error returns to BUG() Some of the error conditions detected by regmap_mmio_*() are pure internal errors, rather than user-/client-triggerable conditions. Convert these to BUG(). Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-mmio.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 1a7b5ee11abc..ffa0e850839e 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -35,8 +35,8 @@ static int regmap_mmio_gather_write(void *context, struct regmap_mmio_context *ctx = context; u32 offset; - if (reg_size != 4) - return -EIO; + BUG_ON(reg_size != 4); + if (val_size % ctx->val_bytes) return -EIO; @@ -60,7 +60,7 @@ static int regmap_mmio_gather_write(void *context, #endif default: /* Should be caught by regmap_mmio_check_config */ - return -EIO; + BUG(); } val_size -= ctx->val_bytes; val += ctx->val_bytes; @@ -72,8 +72,8 @@ static int regmap_mmio_gather_write(void *context, static int regmap_mmio_write(void *context, const void *data, size_t count) { - if (count < 4) - return -EIO; + BUG_ON(count < 4); + return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4); } @@ -84,8 +84,8 @@ static int regmap_mmio_read(void *context, struct regmap_mmio_context *ctx = context; u32 offset; - if (reg_size != 4) - return -EIO; + BUG_ON(reg_size != 4); + if (val_size % ctx->val_bytes) return -EIO; @@ -109,7 +109,7 @@ static int regmap_mmio_read(void *context, #endif default: /* Should be caught by regmap_mmio_check_config */ - return -EIO; + BUG(); } val_size -= ctx->val_bytes; val += ctx->val_bytes; From 9878647f4349dbaa46b4026ed9bbf8acfc0de34c Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 6 Apr 2012 15:17:33 -0600 Subject: [PATCH 07/10] regmap: mmio: remove some error checks now in the core These error checks are implemented in regmap core. Remove the duplicate code from regmap-mmio.c Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-mmio.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index ffa0e850839e..bdf4dc865293 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -37,9 +37,6 @@ static int regmap_mmio_gather_write(void *context, BUG_ON(reg_size != 4); - if (val_size % ctx->val_bytes) - return -EIO; - offset = be32_to_cpup(reg); while (val_size) { @@ -86,9 +83,6 @@ static int regmap_mmio_read(void *context, BUG_ON(reg_size != 4); - if (val_size % ctx->val_bytes) - return -EIO; - offset = be32_to_cpup(reg); while (val_size) { From 851960ba7cb38a6a108d102e4c8b0ab702972e22 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 6 Apr 2012 15:16:03 -0600 Subject: [PATCH 08/10] regmap: validate regmap_raw_read/write val_len val_len should be a multiple of val_bytes. If it's not, error out early. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6b4a775b439b..ee4fea351220 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -595,6 +595,9 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, { int ret; + if (val_len % map->format.val_bytes) + return -EINVAL; + map->lock(map); ret = _regmap_raw_write(map, reg, val, val_len); @@ -753,6 +756,9 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int v; int ret, i; + if (val_len % map->format.val_bytes) + return -EINVAL; + map->lock(map); if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || From d3c242e1f22f5dfed009296ee45ce896153f0b53 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:29 -0600 Subject: [PATCH 09/10] regmap: allow regmap instances to be named Some devices have multiple separate register regions. Logically, one regmap would be created per region. One issue that prevents this is that each instance will attempt to create the same debugfs files. Avoid this by allowing regmaps to be named, and use the name to construct the debugfs directory name. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 3 ++- drivers/base/regmap/regmap-debugfs.c | 14 +++++++++++--- drivers/base/regmap/regmap.c | 4 ++-- include/linux/regmap.h | 5 +++++ 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index fcafc5b2e651..6beef6691c47 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -41,6 +41,7 @@ struct regmap { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; + const char *debugfs_name; #endif unsigned int max_register; @@ -101,7 +102,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, #ifdef CONFIG_DEBUG_FS extern void regmap_debugfs_initcall(void); -extern void regmap_debugfs_init(struct regmap *map); +extern void regmap_debugfs_init(struct regmap *map, const char *name); extern void regmap_debugfs_exit(struct regmap *map); #else static inline void regmap_debugfs_initcall(void) { } diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 58517a5dac13..9715e8e44506 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -248,10 +248,17 @@ static const struct file_operations regmap_access_fops = { .llseek = default_llseek, }; -void regmap_debugfs_init(struct regmap *map) +void regmap_debugfs_init(struct regmap *map, const char *name) { - map->debugfs = debugfs_create_dir(dev_name(map->dev), - regmap_debugfs_root); + if (name) { + map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", + dev_name(map->dev), name); + name = map->debugfs_name; + } else { + name = dev_name(map->dev); + } + + map->debugfs = debugfs_create_dir(name, regmap_debugfs_root); if (!map->debugfs) { dev_warn(map->dev, "Failed to create debugfs directory\n"); return; @@ -280,6 +287,7 @@ void regmap_debugfs_init(struct regmap *map) void regmap_debugfs_exit(struct regmap *map) { debugfs_remove_recursive(map->debugfs); + kfree(map->debugfs_name); } void regmap_debugfs_initcall(void) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7a3f535e481c..b1dad1f9c47d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -289,7 +289,7 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } - regmap_debugfs_init(map); + regmap_debugfs_init(map, config->name); ret = regcache_init(map, config); if (ret < 0) @@ -372,7 +372,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; - regmap_debugfs_init(map); + regmap_debugfs_init(map, config->name); map->cache_bypass = false; map->cache_only = false; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a90abb6bfa64..0a27ee809ca1 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -46,6 +46,9 @@ struct reg_default { /** * Configuration for the register map of a device. * + * @name: Optional name of the regmap. Useful when a device has multiple + * register regions. + * * @reg_bits: Number of bits in a register address, mandatory. * @pad_bits: Number of bits of padding between register and value. * @val_bits: Number of bits in a register value, mandatory. @@ -77,6 +80,8 @@ struct reg_default { * @num_reg_defaults_raw: Number of elements in reg_defaults_raw. */ struct regmap_config { + const char *name; + int reg_bits; int pad_bits; int val_bits; From abec95adefaeb2229cb28de65f3d32cd149b9dd9 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 5 Apr 2012 23:09:20 -0600 Subject: [PATCH 10/10] regmap: fix compilation when !CONFIG_DEBUG_FS Commit 79c64d5 "regmap: allow regmap instances to be named" changed the prototype of regmap_debugfs_init, but didn't update the dummy inline used when !CONFIG_DEBUGFS. Fix this. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 6beef6691c47..8461ca7711ed 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -106,7 +106,7 @@ extern void regmap_debugfs_init(struct regmap *map, const char *name); extern void regmap_debugfs_exit(struct regmap *map); #else static inline void regmap_debugfs_initcall(void) { } -static inline void regmap_debugfs_init(struct regmap *map) { } +static inline void regmap_debugfs_init(struct regmap *map, const char *name) { } static inline void regmap_debugfs_exit(struct regmap *map) { } #endif