From ea279fc5619e2541a0c28196b0fa06447d9ad026 Mon Sep 17 00:00:00 2001 From: Marc Reilly Date: Fri, 16 Mar 2012 12:11:42 +1100 Subject: 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(+) (limited to 'drivers') 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; -- cgit v1.2.3 From d939fb9a78b4743bc4bc3cc415894ed42050c5cc Mon Sep 17 00:00:00 2001 From: Marc Reilly Date: Fri, 16 Mar 2012 12:11:43 +1100 Subject: 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(-) (limited to 'drivers') 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); } } -- cgit v1.2.3 From 0135bbcc7a0cc056f0203ff839466236b8e3dc19 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:30 -0600 Subject: 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 +++++++++++++------ 4 files changed, 30 insertions(+), 16 deletions(-) (limited to 'drivers') 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); -- cgit v1.2.3 From bacdbe077342ecc9e7b3e374cc5a41995116706a Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:28 -0600 Subject: 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 +++++++++++++++++++++++++---------- 4 files changed, 64 insertions(+), 30 deletions(-) (limited to 'drivers') 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 @@ out: 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 @@ out: 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 @@ out: */ 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; } -- cgit v1.2.3 From 45f5ff8107a845854b1d1812ab1d9c5541f08b4d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:31 -0600 Subject: 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 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 drivers/base/regmap/regmap-mmio.c (limited to 'drivers') 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"); -- cgit v1.2.3 From 40606dba450830e50420599c52a86cf6ce5c6a14 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 6 Apr 2012 15:17:32 -0600 Subject: 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(-) (limited to 'drivers') 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; -- cgit v1.2.3 From 9878647f4349dbaa46b4026ed9bbf8acfc0de34c Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 6 Apr 2012 15:17:33 -0600 Subject: 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(-) (limited to 'drivers') 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) { -- cgit v1.2.3 From 851960ba7cb38a6a108d102e4c8b0ab702972e22 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 6 Apr 2012 15:16:03 -0600 Subject: 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(+) (limited to 'drivers') 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 || -- cgit v1.2.3 From d3c242e1f22f5dfed009296ee45ce896153f0b53 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:29 -0600 Subject: 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 ++-- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers') 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; -- cgit v1.2.3 From abec95adefaeb2229cb28de65f3d32cd149b9dd9 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 5 Apr 2012 23:09:20 -0600 Subject: 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(-) (limited to 'drivers') 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 -- cgit v1.2.3 From f01ee60fffa4dc6c77122121233a793f7f696e67 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 9 Apr 2012 13:40:24 -0600 Subject: regmap: implement register striding regmap_config.reg_stride is introduced. All extant register addresses are a multiple of this value. Users of serial-oriented regmap busses will typically set this to 1. Users of the MMIO regmap bus will typically set this based on the value size of their registers, in bytes, so 4 for a 32-bit register. Throughout the regmap code, actual register addresses are used. Wherever the register address is used to index some array of values, the address is divided by the stride to determine the index, or vice-versa. Error- checking is added to all entry-points for register address data to ensure that register addresses actually satisfy the specified stride. The MMIO bus ensures that the specified stride is large enough for the register size. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache-lzo.c | 11 +++++----- drivers/base/regmap/regcache-rbtree.c | 40 ++++++++++++++++++++--------------- drivers/base/regmap/regcache.c | 14 +++++++++--- drivers/base/regmap/regmap-debugfs.c | 4 ++-- drivers/base/regmap/regmap-irq.c | 34 +++++++++++++++++++---------- drivers/base/regmap/regmap-mmio.c | 13 ++++++++++++ drivers/base/regmap/regmap.c | 30 ++++++++++++++++++++++---- 8 files changed, 105 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 99b28fffbd0e..d92e9b1cb83c 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -62,6 +62,7 @@ struct regmap { /* number of bits to (left) shift the reg value when formatting*/ int reg_shift; + int reg_stride; /* regcache specific members */ const struct regcache_ops *cache_ops; diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 483b06d4a380..afd6aa91a0df 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -108,7 +108,7 @@ static int regcache_lzo_decompress_cache_block(struct regmap *map, static inline int regcache_lzo_get_blkindex(struct regmap *map, unsigned int reg) { - return (reg * map->cache_word_size) / + return ((reg / map->reg_stride) * map->cache_word_size) / DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count(map)); } @@ -116,9 +116,10 @@ static inline int regcache_lzo_get_blkindex(struct regmap *map, static inline int regcache_lzo_get_blkpos(struct regmap *map, unsigned int reg) { - return reg % (DIV_ROUND_UP(map->cache_size_raw, - regcache_lzo_block_count(map)) / - map->cache_word_size); + return (reg / map->reg_stride) % + (DIV_ROUND_UP(map->cache_size_raw, + regcache_lzo_block_count(map)) / + map->cache_word_size); } static inline int regcache_lzo_get_blksize(struct regmap *map) @@ -322,7 +323,7 @@ static int regcache_lzo_write(struct regmap *map, } /* set the bit so we know we have to sync this register */ - set_bit(reg, lzo_block->sync_bmp); + set_bit(reg / map->reg_stride, lzo_block->sync_bmp); kfree(tmp_dst); kfree(lzo_block->src); return 0; diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index e49e71fab184..e6732cf7c06e 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -39,11 +39,12 @@ struct regcache_rbtree_ctx { }; static inline void regcache_rbtree_get_base_top_reg( + struct regmap *map, struct regcache_rbtree_node *rbnode, unsigned int *base, unsigned int *top) { *base = rbnode->base_reg; - *top = rbnode->base_reg + rbnode->blklen - 1; + *top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride); } static unsigned int regcache_rbtree_get_register( @@ -70,7 +71,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, rbnode = rbtree_ctx->cached_rbnode; if (rbnode) { - regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); if (reg >= base_reg && reg <= top_reg) return rbnode; } @@ -78,7 +80,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, node = rbtree_ctx->root.rb_node; while (node) { rbnode = container_of(node, struct regcache_rbtree_node, node); - regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); if (reg >= base_reg && reg <= top_reg) { rbtree_ctx->cached_rbnode = rbnode; return rbnode; @@ -92,7 +95,7 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, return NULL; } -static int regcache_rbtree_insert(struct rb_root *root, +static int regcache_rbtree_insert(struct regmap *map, struct rb_root *root, struct regcache_rbtree_node *rbnode) { struct rb_node **new, *parent; @@ -106,7 +109,7 @@ static int regcache_rbtree_insert(struct rb_root *root, rbnode_tmp = container_of(*new, struct regcache_rbtree_node, node); /* base and top registers of the current rbnode */ - regcache_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp, + regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp, &top_reg_tmp); /* base register of the rbnode to be added */ base_reg = rbnode->base_reg; @@ -138,7 +141,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) unsigned int base, top; int nodes = 0; int registers = 0; - int average; + int this_registers, average; map->lock(map); @@ -146,11 +149,12 @@ static int rbtree_show(struct seq_file *s, void *ignored) node = rb_next(node)) { n = container_of(node, struct regcache_rbtree_node, node); - regcache_rbtree_get_base_top_reg(n, &base, &top); - seq_printf(s, "%x-%x (%d)\n", base, top, top - base + 1); + regcache_rbtree_get_base_top_reg(map, n, &base, &top); + this_registers = ((top - base) / map->reg_stride) + 1; + seq_printf(s, "%x-%x (%d)\n", base, top, this_registers); nodes++; - registers += top - base + 1; + registers += this_registers; } if (nodes) @@ -255,7 +259,7 @@ static int regcache_rbtree_read(struct regmap *map, rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { - reg_tmp = reg - rbnode->base_reg; + reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; *value = regcache_rbtree_get_register(rbnode, reg_tmp, map->cache_word_size); } else { @@ -310,7 +314,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, */ rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { - reg_tmp = reg - rbnode->base_reg; + reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; val = regcache_rbtree_get_register(rbnode, reg_tmp, map->cache_word_size); if (val == value) @@ -321,13 +325,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, /* look for an adjacent register to the one we are about to add */ for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { - rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node); + rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, + node); for (i = 0; i < rbnode_tmp->blklen; i++) { - reg_tmp = rbnode_tmp->base_reg + i; - if (abs(reg_tmp - reg) != 1) + reg_tmp = rbnode_tmp->base_reg + + (i * map->reg_stride); + if (abs(reg_tmp - reg) != map->reg_stride) continue; /* decide where in the block to place our register */ - if (reg_tmp + 1 == reg) + if (reg_tmp + map->reg_stride == reg) pos = i + 1; else pos = i; @@ -357,7 +363,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, return -ENOMEM; } regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size); - regcache_rbtree_insert(&rbtree_ctx->root, rbnode); + regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode); rbtree_ctx->cached_rbnode = rbnode; } @@ -397,7 +403,7 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, end = rbnode->blklen; for (i = base; i < end; i++) { - regtmp = rbnode->base_reg + i; + regtmp = rbnode->base_reg + (i * map->reg_stride); val = regcache_rbtree_get_register(rbnode, i, map->cache_word_size); diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index d4368e8b6f9d..835883bda977 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -59,7 +59,7 @@ static int regcache_hw_init(struct regmap *map) for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (regmap_volatile(map, i)) + if (regmap_volatile(map, i * map->reg_stride)) continue; count++; } @@ -76,9 +76,9 @@ static int regcache_hw_init(struct regmap *map) for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (regmap_volatile(map, i)) + if (regmap_volatile(map, i * map->reg_stride)) continue; - map->reg_defaults[j].reg = i; + map->reg_defaults[j].reg = i * map->reg_stride; map->reg_defaults[j].def = val; j++; } @@ -98,6 +98,10 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) int i; void *tmp_buf; + for (i = 0; i < config->num_reg_defaults; i++) + if (config->reg_defaults[i].reg % map->reg_stride) + return -EINVAL; + if (map->cache_type == REGCACHE_NONE) { map->cache_bypass = true; return 0; @@ -278,6 +282,10 @@ int regcache_sync(struct regmap *map) /* Apply any patch first */ map->cache_bypass = 1; for (i = 0; i < map->patch_regs; i++) { + if (map->patch[i].reg % map->reg_stride) { + ret = -EINVAL; + goto out; + } ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def); if (ret != 0) { dev_err(map->dev, "Failed to write %x = %x: %d\n", diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index df97c93efa8e..bb1ff175b962 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -80,7 +80,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, val_len = 2 * map->format.val_bytes; tot_len = reg_len + val_len + 3; /* : \n */ - for (i = 0; i < map->max_register + 1; i++) { + for (i = 0; i <= map->max_register; i += map->reg_stride) { if (!regmap_readable(map, i)) continue; @@ -197,7 +197,7 @@ static ssize_t regmap_access_read_file(struct file *file, reg_len = regmap_calc_reg_len(map->max_register, buf, count); tot_len = reg_len + 10; /* ': R W V P\n' */ - for (i = 0; i < map->max_register + 1; i++) { + for (i = 0; i <= map->max_register; i += map->reg_stride) { /* Ignore registers which are neither readable nor writable */ if (!regmap_readable(map, i) && !regmap_writeable(map, i)) continue; diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 1befaa7a31cb..56b8136eb36a 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -58,11 +58,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data) * suppress pointless writes. */ for (i = 0; i < d->chip->num_regs; i++) { - ret = regmap_update_bits(d->map, d->chip->mask_base + i, + ret = regmap_update_bits(d->map, d->chip->mask_base + + (i * map->map->reg_stride), d->mask_buf_def[i], d->mask_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", - d->chip->mask_base + i); + d->chip->mask_base + (i * map->reg_stride)); } mutex_unlock(&d->lock); @@ -73,7 +74,7 @@ static void regmap_irq_enable(struct irq_data *data) struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); - d->mask_buf[irq_data->reg_offset] &= ~irq_data->mask; + d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; } static void regmap_irq_disable(struct irq_data *data) @@ -81,7 +82,7 @@ static void regmap_irq_disable(struct irq_data *data) struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); - d->mask_buf[irq_data->reg_offset] |= irq_data->mask; + d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; } static struct irq_chip regmap_irq_chip = { @@ -136,17 +137,19 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) data->status_buf[i] &= ~data->mask_buf[i]; if (data->status_buf[i] && chip->ack_base) { - ret = regmap_write(map, chip->ack_base + i, + ret = regmap_write(map, chip->ack_base + + (i * map->reg_stride), data->status_buf[i]); if (ret != 0) dev_err(map->dev, "Failed to ack 0x%x: %d\n", - chip->ack_base + i, ret); + chip->ack_base + (i * map->reg_stride), + ret); } } for (i = 0; i < chip->num_irqs; i++) { - if (data->status_buf[chip->irqs[i].reg_offset] & - chip->irqs[i].mask) { + if (data->status_buf[chip->irqs[i].reg_offset / + map->reg_stride] & chip->irqs[i].mask) { handle_nested_irq(data->irq_base + i); handled = true; } @@ -181,6 +184,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, int cur_irq, i; int ret = -ENOMEM; + for (i = 0; i < chip->num_irqs; i++) { + if (chip->irqs[i].reg_offset % map->reg_stride) + return -EINVAL; + if (chip->irqs[i].reg_offset / map->reg_stride >= + chip->num_regs) + return -EINVAL; + } + irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); if (irq_base < 0) { dev_warn(map->dev, "Failed to allocate IRQs: %d\n", @@ -218,16 +229,17 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, mutex_init(&d->lock); for (i = 0; i < chip->num_irqs; i++) - d->mask_buf_def[chip->irqs[i].reg_offset] + d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] |= chip->irqs[i].mask; /* Mask all the interrupts by default */ for (i = 0; i < chip->num_regs; i++) { d->mask_buf[i] = d->mask_buf_def[i]; - ret = regmap_write(map, chip->mask_base + i, d->mask_buf[i]); + ret = regmap_write(map, chip->mask_base + (i * map->reg_stride), + d->mask_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", - chip->mask_base + i, ret); + chip->mask_base + (i * map->reg_stride), ret); goto err_alloc; } } diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index bdf4dc865293..febd6de6c8ac 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -130,6 +130,7 @@ struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, const struct regmap_config *config) { struct regmap_mmio_context *ctx; + int min_stride; if (config->reg_bits != 32) return ERR_PTR(-EINVAL); @@ -139,16 +140,28 @@ struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, switch (config->val_bits) { case 8: + /* The core treats 0 as 1 */ + min_stride = 0; + break; case 16: + min_stride = 2; + break; case 32: + min_stride = 4; + break; #ifdef CONFIG_64BIT case 64: + min_stride = 8; + break; #endif break; default: return ERR_PTR(-EINVAL); } + if (config->reg_stride < min_stride) + return ERR_PTR(-EINVAL); + ctx = kzalloc(GFP_KERNEL, sizeof(*ctx)); if (!ctx) return ERR_PTR(-ENOMEM); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 40f910162781..8a25006b2a4d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -243,6 +243,10 @@ struct regmap *regmap_init(struct device *dev, 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; + if (config->reg_stride) + map->reg_stride = config->reg_stride; + else + map->reg_stride = 1; map->dev = dev; map->bus = bus; map->bus_context = bus_context; @@ -469,7 +473,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, /* 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)) + if (!map->writeable_reg(map->dev, + reg + (i * map->reg_stride))) return -EINVAL; if (!map->cache_bypass && map->format.parse_val) { @@ -478,7 +483,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, for (i = 0; i < val_len / val_bytes; i++) { memcpy(map->work_buf, val + (i * val_bytes), val_bytes); ival = map->format.parse_val(map->work_buf); - ret = regcache_write(map, reg + i, ival); + ret = regcache_write(map, reg + (i * map->reg_stride), + ival); if (ret) { dev_err(map->dev, "Error in caching of register: %u ret: %d\n", @@ -590,6 +596,9 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; + if (reg % map->reg_stride) + return -EINVAL; + map->lock(map); ret = _regmap_write(map, reg, val); @@ -623,6 +632,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, if (val_len % map->format.val_bytes) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; map->lock(map); @@ -657,6 +668,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (!map->format.parse_val) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; map->lock(map); @@ -753,6 +766,9 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; + if (reg % map->reg_stride) + return -EINVAL; + map->lock(map); ret = _regmap_read(map, reg, val); @@ -784,6 +800,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, if (val_len % map->format.val_bytes) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; map->lock(map); @@ -797,7 +815,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, * cost as we expect to hit the cache. */ for (i = 0; i < val_count; i++) { - ret = _regmap_read(map, reg + i, &v); + ret = _regmap_read(map, reg + (i * map->reg_stride), + &v); if (ret != 0) goto out; @@ -832,6 +851,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, if (!map->format.parse_val) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; if (vol || map->cache_type == REGCACHE_NONE) { ret = regmap_raw_read(map, reg, val, val_bytes * val_count); @@ -842,7 +863,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, map->format.parse_val(val + i); } else { for (i = 0; i < val_count; i++) { - ret = regmap_read(map, reg + i, val + (i * val_bytes)); + ret = regmap_read(map, reg + (i * map->reg_stride), + val + (i * val_bytes)); if (ret != 0) return ret; } -- cgit v1.2.3 From 56806555de5485d6786bf0f8df01b8ed9fc5d006 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 10 Apr 2012 23:37:22 -0600 Subject: regmap: fix compile errors in regmap-irq.c due to stride changes Commit f01ee60fffa4 ("regmap: implement register striding") caused the compile errors below. Fix them. drivers/base/regmap/regmap-irq.c: In function 'regmap_irq_sync_unlock': drivers/base/regmap/regmap-irq.c:62:12: error: 'map' undeclared (first use in this function) drivers/base/regmap/regmap-irq.c:62:12: note: each undeclared identifier is reported only once for each function it appears in drivers/base/regmap/regmap-irq.c: In function 'regmap_irq_enable': drivers/base/regmap/regmap-irq.c:77:37: error: 'map' undeclared (first use in this function) drivers/base/regmap/regmap-irq.c: In function 'regmap_irq_disable': drivers/base/regmap/regmap-irq.c:85:37: error: 'map' undeclared (first use in this function) Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 56b8136eb36a..fc69d29d272a 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -50,6 +50,7 @@ static void regmap_irq_lock(struct irq_data *data) static void regmap_irq_sync_unlock(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + struct regmap *map = d->map; int i, ret; /* @@ -59,7 +60,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) */ for (i = 0; i < d->chip->num_regs; i++) { ret = regmap_update_bits(d->map, d->chip->mask_base + - (i * map->map->reg_stride), + (i * map->reg_stride), d->mask_buf_def[i], d->mask_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", @@ -72,6 +73,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) static void regmap_irq_enable(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + struct regmap *map = d->map; const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; @@ -80,6 +82,7 @@ static void regmap_irq_enable(struct irq_data *data) static void regmap_irq_disable(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + struct regmap *map = d->map; const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; -- cgit v1.2.3 From f298536728d02c19f11bda8d712ff61d767bab32 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 30 Apr 2012 21:25:05 +0100 Subject: regmap: Cache single values read from the chip If we don't have a cached value for a register and we can cache it then when we do a read a value we should add it to the cache to save rereading it later on. Do this for single register reads, for block reads the code would be a little more complex and this covers most practical usage. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 178989a8949e..3e551223f4df 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -698,6 +698,9 @@ static int _regmap_read(struct regmap *map, unsigned int reg, trace_regmap_reg_read(map->dev, reg, *val); } + if (ret == 0 && !map->cache_bypass) + regcache_write(map, reg, *val); + return ret; } -- cgit v1.2.3 From 2e33caf16f7a1903d226ef7f9f5ec6a234fee18e Mon Sep 17 00:00:00 2001 From: Ashish Jangam Date: Mon, 30 Apr 2012 23:23:40 +0100 Subject: regmap: Converts group operation into single read write operations Some devices does not support bulk read and write operations, for them we have series of single write and read operations. Signed-off-by: Anthony Olech Signed-off-by: Ashish Jangam [Fixed coding style, don't check use_single_rw before assign --broonie ] Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 3 +++ drivers/base/regmap/regmap.c | 40 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index d92e9b1cb83c..2eb719704885 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -91,6 +91,9 @@ struct regmap { struct reg_default *patch; int patch_regs; + + /* if set, converts bulk rw to single rw */ + bool use_single_rw; }; struct regcache_ops { diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 8a25006b2a4d..0a05a706e141 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -247,6 +247,7 @@ struct regmap *regmap_init(struct device *dev, map->reg_stride = config->reg_stride; else map->reg_stride = 1; + map->use_single_rw = config->use_single_rw; map->dev = dev; map->bus = bus; map->bus_context = bus_context; @@ -686,7 +687,22 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, for (i = 0; i < val_count * val_bytes; i += val_bytes) map->format.parse_val(wval + i); } - ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); + /* + * Some devices does not support bulk write, for + * them we have a series of single write operations. + */ + if (map->use_single_rw) { + for (i = 0; i < val_count; i++) { + ret = regmap_raw_write(map, + reg + (i * map->reg_stride), + val + (i * val_bytes), + val_bytes); + if (ret != 0) + return ret; + } + } else { + ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); + } if (val_bytes != 1) kfree(wval); @@ -855,9 +871,25 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, return -EINVAL; if (vol || map->cache_type == REGCACHE_NONE) { - ret = regmap_raw_read(map, reg, val, val_bytes * val_count); - if (ret != 0) - return ret; + /* + * Some devices does not support bulk read, for + * them we have a series of single read operations. + */ + if (map->use_single_rw) { + for (i = 0; i < val_count; i++) { + ret = regmap_raw_read(map, + reg + (i * map->reg_stride), + val + (i * val_bytes), + val_bytes); + if (ret != 0) + return ret; + } + } else { + ret = regmap_raw_read(map, reg, val, + val_bytes * val_count); + if (ret != 0) + return ret; + } for (i = 0; i < val_count * val_bytes; i += val_bytes) map->format.parse_val(val + i); -- cgit v1.2.3 From 7a6476143270d947924f5bbbc124accb0e558bf4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 30 Apr 2012 23:26:32 +0100 Subject: regmap: Devices using format_write don't support bulk operations Set the use_single_rw flag for devices that use format_write() since format_write() doesn't support any form of block operation. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 0a05a706e141..fcd69ff695d5 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -341,6 +341,9 @@ struct regmap *regmap_init(struct device *dev, break; } + if (map->format.format_write) + map->use_single_rw = true; + if (!map->format.format_write && !(map->format.format_reg && map->format.format_val)) goto err_map; -- cgit v1.2.3 From 72b39f6f2b5a6b0beff14b80bed9756f151218a9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 May 2012 17:44:40 +0100 Subject: regmap: Implement dev_get_regmap() Use devres to implement dev_get_regmap(). This should mean that in almost all cases devices wishing to take advantage of framework features based on regmap shouldn't need to explicitly pass the regmap into the framework. This simplifies device setup a bit. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap.c | 61 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index e46c279f8280..c994bc9bc04f 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -45,6 +45,7 @@ struct regmap { struct regmap_format format; /* Buffer format */ const struct regmap_bus *bus; void *bus_context; + const char *name; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ee4fea351220..618173e4c2b1 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -178,6 +178,15 @@ static void regmap_unlock_spinlock(struct regmap *map) spin_unlock(&map->spinlock); } +static void dev_get_regmap_release(struct device *dev, void *res) +{ + /* + * We don't actually have anything to do here; the goal here + * is not to manage the regmap but to provide a simple way to + * get the regmap back given a struct device. + */ +} + /** * regmap_init(): Initialise register map * @@ -195,7 +204,7 @@ struct regmap *regmap_init(struct device *dev, void *bus_context, const struct regmap_config *config) { - struct regmap *map; + struct regmap *map, **m; int ret = -EINVAL; if (!bus || !config) @@ -230,6 +239,7 @@ struct regmap *regmap_init(struct device *dev, map->volatile_reg = config->volatile_reg; map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; + map->name = config->name; if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; @@ -326,8 +336,19 @@ struct regmap *regmap_init(struct device *dev, if (ret < 0) goto err_free_workbuf; + /* Add a devres resource for dev_get_regmap() */ + m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); + if (!m) { + ret = -ENOMEM; + goto err_cache; + } + *m = map; + devres_add(dev, m); + return map; +err_cache: + regcache_exit(map); err_free_workbuf: kfree(map->work_buf); err_map: @@ -431,6 +452,44 @@ void regmap_exit(struct regmap *map) } EXPORT_SYMBOL_GPL(regmap_exit); +static int dev_get_regmap_match(struct device *dev, void *res, void *data) +{ + struct regmap **r = res; + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + + /* If the user didn't specify a name match any */ + if (data) + return (*r)->name == data; + else + return 1; +} + +/** + * dev_get_regmap(): Obtain the regmap (if any) for a device + * + * @dev: Device to retrieve the map for + * @name: Optional name for the register map, usually NULL. + * + * Returns the regmap for the device if one is present, or NULL. If + * name is specified then it must match the name specified when + * registering the device, if it is NULL then the first regmap found + * will be used. Devices with multiple register maps are very rare, + * generic code should normally not need to specify a name. + */ +struct regmap *dev_get_regmap(struct device *dev, const char *name) +{ + struct regmap **r = devres_find(dev, dev_get_regmap_release, + dev_get_regmap_match, (void *)name); + + if (!r) + return NULL; + return *r; +} +EXPORT_SYMBOL_GPL(dev_get_regmap); + static int _regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { -- cgit v1.2.3 From 8614419451d88bf99fff7f5e468fe45f8450891e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 13 May 2012 18:53:23 +0100 Subject: mfd: da9052: Fix genirq abuse Rather than using the pointer passed back by the regmap API (or complaining because that wasn't actually being set) the da9052 driver was having some fun and games peering through genirq and regmap internals. Fix the driver to use the API as expected. Signed-off-by: Mark Brown --- drivers/mfd/da9052-core.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index 7ff313fe9fb1..7776aff46269 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -659,12 +659,11 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, da9052->irq_base, &da9052_regmap_irq_chip, - NULL); + &da9052->irq_data); if (ret < 0) goto regmap_err; - desc = irq_to_desc(da9052->chip_irq); - da9052->irq_base = regmap_irq_chip_get_base(desc->action->dev_id); + da9052->irq_base = regmap_irq_chip_get_base(da9052->irq_data); ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info, ARRAY_SIZE(da9052_subdev_info), NULL, 0); @@ -681,8 +680,7 @@ regmap_err: void da9052_device_exit(struct da9052 *da9052) { - regmap_del_irq_chip(da9052->chip_irq, - irq_get_irq_data(da9052->irq_base)->chip_data); + regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); mfd_remove_devices(da9052->dev); } -- cgit v1.2.3 From 2431d0a1d68aabefeee02b93971ee73e8b215697 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 13 May 2012 11:18:34 +0100 Subject: regmap: Pass back the allocated regmap IRQ controller data It's needed for freeing and for obtaining the IRQ base later on. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 1befaa7a31cb..f4e6dcfc8504 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -192,6 +192,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!d) return -ENOMEM; + *data = d; + d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, GFP_KERNEL); if (!d->status_buf) -- cgit v1.2.3 From 4af8be67fd9989f4e63a8d1defc1895ed0f7d341 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 13 May 2012 10:59:56 +0100 Subject: regmap: Convert regmap_irq to use irq_domain This gets us up to date with the recommended current kernel infrastructure and should transparently give us device tree interrupt bindings for any devices using the framework. If an explicit IRQ mapping is passed in then a legacy interrupt range is created, otherwise a simple linear mapping is used. Previously a mapping was mandatory so existing drivers should not be affected. A function regmap_irq_get_virq() is provided to allow drivers to map individual IRQs which should be used in preference to the existing regmap_irq_chip_get_base() which is only valid if a legacy IRQ range is provided. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 97 +++++++++++++++++++++++++++++----------- drivers/mfd/Kconfig | 3 ++ 2 files changed, 73 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 0d233cc1c7fa..db5305b37c9d 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "internal.h" @@ -26,6 +27,7 @@ struct regmap_irq_chip_data { struct regmap_irq_chip *chip; int irq_base; + struct irq_domain *domain; void *status_reg_buf; unsigned int *status_buf; @@ -37,7 +39,7 @@ static inline const struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, int irq) { - return &data->chip->irqs[irq - data->irq_base]; + return &data->chip->irqs[irq]; } static void regmap_irq_lock(struct irq_data *data) @@ -74,7 +76,7 @@ static void regmap_irq_enable(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap *map = d->map; - const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); + const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; } @@ -83,7 +85,7 @@ static void regmap_irq_disable(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap *map = d->map; - const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); + const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; } @@ -153,7 +155,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) for (i = 0; i < chip->num_irqs; i++) { if (data->status_buf[chip->irqs[i].reg_offset / map->reg_stride] & chip->irqs[i].mask) { - handle_nested_irq(data->irq_base + i); + handle_nested_irq(irq_find_mapping(data->domain, i)); handled = true; } } @@ -164,6 +166,31 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) return IRQ_NONE; } +static int regmap_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct regmap_irq_chip_data *data = h->host_data; + + irq_set_chip_data(virq, data); + irq_set_chip_and_handler(virq, ®map_irq_chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(virq, IRQF_VALID); +#else + irq_set_noprobe(virq); +#endif + + return 0; +} + +static struct irq_domain_ops regmap_domain_ops = { + .map = regmap_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + /** * regmap_add_irq_chip(): Use standard regmap IRQ controller handling * @@ -184,7 +211,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, struct regmap_irq_chip_data **data) { struct regmap_irq_chip_data *d; - int cur_irq, i; + int i; int ret = -ENOMEM; for (i = 0; i < chip->num_irqs; i++) { @@ -195,11 +222,13 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, return -EINVAL; } - irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); - if (irq_base < 0) { - dev_warn(map->dev, "Failed to allocate IRQs: %d\n", - irq_base); - return irq_base; + if (irq_base) { + irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); + if (irq_base < 0) { + dev_warn(map->dev, "Failed to allocate IRQs: %d\n", + irq_base); + return irq_base; + } } d = kzalloc(sizeof(*d), GFP_KERNEL); @@ -249,33 +278,31 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, } } - /* Register them with genirq */ - for (cur_irq = irq_base; - cur_irq < chip->num_irqs + irq_base; - cur_irq++) { - irq_set_chip_data(cur_irq, d); - irq_set_chip_and_handler(cur_irq, ®map_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); - - /* ARM needs us to explicitly flag the IRQ as valid - * and will set them noprobe when we do so. */ -#ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif + if (irq_base) + d->domain = irq_domain_add_legacy(map->dev->of_node, + chip->num_irqs, irq_base, 0, + ®map_domain_ops, d); + else + d->domain = irq_domain_add_linear(map->dev->of_node, + chip->num_irqs, + ®map_domain_ops, d); + if (!d->domain) { + dev_err(map->dev, "Failed to create IRQ domain\n"); + ret = -ENOMEM; + goto err_alloc; } ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags, chip->name, d); if (ret != 0) { dev_err(map->dev, "Failed to request IRQ %d: %d\n", irq, ret); - goto err_alloc; + goto err_domain; } return 0; +err_domain: + /* Should really dispose of the domain but... */ err_alloc: kfree(d->mask_buf_def); kfree(d->mask_buf); @@ -298,6 +325,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) return; free_irq(irq, d); + /* We should unmap the domain but... */ kfree(d->mask_buf_def); kfree(d->mask_buf); kfree(d->status_reg_buf); @@ -315,6 +343,21 @@ EXPORT_SYMBOL_GPL(regmap_del_irq_chip); */ int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data) { + WARN_ON(!data->irq_base); return data->irq_base; } EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); + +/** + * regmap_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ + * + * Useful for drivers to request their own IRQs. + * + * @data: regmap_irq controller to operate on. + * @irq: index of the interrupt requested in the chip IRQs + */ +int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) +{ + return irq_create_mapping(data->domain, irq); +} +EXPORT_SYMBOL_GPL(regmap_irq_get_virq); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 11e44386fa9b..697d686927f8 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -376,6 +376,7 @@ config PMIC_DA9052 config MFD_DA9052_SPI bool "Support Dialog Semiconductor DA9052/53 PMIC variants with SPI" + select IRQ_DOMAIN select REGMAP_SPI select REGMAP_IRQ select PMIC_DA9052 @@ -388,6 +389,7 @@ config MFD_DA9052_SPI config MFD_DA9052_I2C bool "Support Dialog Semiconductor DA9052/53 PMIC variants with I2C" + select IRQ_DOMAIN select REGMAP_I2C select REGMAP_IRQ select PMIC_DA9052 @@ -558,6 +560,7 @@ config MFD_WM8994 bool "Support Wolfson Microelectronics WM8994" select MFD_CORE select REGMAP_I2C + select IRQ_DOMAIN select REGMAP_IRQ depends on I2C=y && GENERIC_HARDIRQS help -- cgit v1.2.3 From 022f926a2401c80ed36ebb48a1bffbac08f34d98 Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Mon, 14 May 2012 22:40:43 +0900 Subject: regmap: add support for non contiguous status to regmap-irq In some chips the IRQ status registers are not contiguous in the register map but spaced at even spaces. This is an easy case to handle with minor changes. It is assume for this purpose that the stride for status is equal to the stride for mask/ack registers as well. Signed-off-by: Graeme Gregory Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 54 ++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index db5305b37c9d..502cbdbab638 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -29,10 +29,11 @@ struct regmap_irq_chip_data { int irq_base; struct irq_domain *domain; - void *status_reg_buf; unsigned int *status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; + + unsigned int irq_reg_stride; }; static inline const @@ -62,7 +63,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data) */ for (i = 0; i < d->chip->num_regs; i++) { ret = regmap_update_bits(d->map, d->chip->mask_base + - (i * map->reg_stride), + (i * map->reg_stride * + d->irq_reg_stride), d->mask_buf_def[i], d->mask_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", @@ -104,18 +106,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) struct regmap_irq_chip *chip = data->chip; struct regmap *map = data->map; int ret, i; - u8 *buf8 = data->status_reg_buf; - u16 *buf16 = data->status_reg_buf; - u32 *buf32 = data->status_reg_buf; bool handled = false; - ret = regmap_bulk_read(map, chip->status_base, data->status_reg_buf, - chip->num_regs); - if (ret != 0) { - dev_err(map->dev, "Failed to read IRQ status: %d\n", ret); - return IRQ_NONE; - } - /* * Ignore masked IRQs and ack if we need to; we ack early so * there is no race between handling and acknowleding the @@ -124,18 +116,13 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) * doing a write per register. */ for (i = 0; i < data->chip->num_regs; i++) { - switch (map->format.val_bytes) { - case 1: - data->status_buf[i] = buf8[i]; - break; - case 2: - data->status_buf[i] = buf16[i]; - break; - case 4: - data->status_buf[i] = buf32[i]; - break; - default: - BUG(); + ret = regmap_read(map, chip->mask_base + (i * map->reg_stride + * data->irq_reg_stride), + &data->status_buf[i]); + + if (ret != 0) { + dev_err(map->dev, "Failed to read IRQ status: %d\n", + ret); return IRQ_NONE; } @@ -143,7 +130,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) if (data->status_buf[i] && chip->ack_base) { ret = regmap_write(map, chip->ack_base + - (i * map->reg_stride), + (i * map->reg_stride * + data->irq_reg_stride), data->status_buf[i]); if (ret != 0) dev_err(map->dev, "Failed to ack 0x%x: %d\n", @@ -242,11 +230,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!d->status_buf) goto err_alloc; - d->status_reg_buf = kzalloc(map->format.val_bytes * chip->num_regs, - GFP_KERNEL); - if (!d->status_reg_buf) - goto err_alloc; - d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, GFP_KERNEL); if (!d->mask_buf) @@ -260,6 +243,12 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->map = map; d->chip = chip; d->irq_base = irq_base; + + if (chip->irq_reg_stride) + d->irq_reg_stride = chip->irq_reg_stride; + else + d->irq_reg_stride = 1; + mutex_init(&d->lock); for (i = 0; i < chip->num_irqs; i++) @@ -269,7 +258,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, /* Mask all the interrupts by default */ for (i = 0; i < chip->num_regs; i++) { d->mask_buf[i] = d->mask_buf_def[i]; - ret = regmap_write(map, chip->mask_base + (i * map->reg_stride), + ret = regmap_write(map, chip->mask_base + (i * map->reg_stride + * d->irq_reg_stride), d->mask_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", @@ -306,7 +296,6 @@ err_domain: err_alloc: kfree(d->mask_buf_def); kfree(d->mask_buf); - kfree(d->status_reg_buf); kfree(d->status_buf); kfree(d); return ret; @@ -328,7 +317,6 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) /* We should unmap the domain but... */ kfree(d->mask_buf_def); kfree(d->mask_buf); - kfree(d->status_reg_buf); kfree(d->status_buf); kfree(d); } -- cgit v1.2.3 From 6550334f96ab02e540572a142d0851c7f3120585 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 13 May 2012 11:03:26 +0100 Subject: mfd: wm8994: Update to fully use irq_domain Take advantage of the new regmap irq_domain support to dynamically allocate interrupts, using regmap_irq_get_virq() rather than irq_base to look up the interrupts. This means that most users should not need to specify an irq_base at all. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz --- drivers/mfd/wm8994-irq.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index 46b20c445ecf..f1837f669755 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c @@ -147,12 +147,6 @@ int wm8994_irq_init(struct wm8994 *wm8994) return 0; } - if (!wm8994->irq_base) { - dev_err(wm8994->dev, - "No interrupt base specified, no interrupts\n"); - return 0; - } - ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, wm8994->irq_base, &wm8994_irq_chip, -- cgit v1.2.3 From 38e7f5d1b73e71f87745a9c3e5806a6c28c34a53 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 May 2012 13:59:40 +0100 Subject: regmap: Fix typo in IRQ register striding Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 502cbdbab638..4fac4b9be88f 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -116,7 +116,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) * doing a write per register. */ for (i = 0; i < data->chip->num_regs; i++) { - ret = regmap_read(map, chip->mask_base + (i * map->reg_stride + ret = regmap_read(map, chip->status_base + (i * map->reg_stride * data->irq_reg_stride), &data->status_buf[i]); -- cgit v1.2.3 From 2945fbc2fcd83df03165342c1bc3ab83d0fe9c04 Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Tue, 15 May 2012 15:48:56 +0900 Subject: mfd: palmas PMIC device support Palmas is a PMIC from Texas Instruments and this is the MFD part of the driver for this chip. The PMIC has SMPS and LDO regulators, a general purpose ADC, GPIO, USB OTG mode detection, watchdog and RTC features. Signed-off-by: Graeme Gregory Acked-by: Samuel Ortiz Signed-off-by: Mark Brown --- drivers/mfd/palmas.c | 509 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 509 insertions(+) create mode 100644 drivers/mfd/palmas.c (limited to 'drivers') diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c new file mode 100644 index 000000000000..00c0aba7eba0 --- /dev/null +++ b/drivers/mfd/palmas.c @@ -0,0 +1,509 @@ +/* + * TI Palmas MFD Driver + * + * Copyright 2011-2012 Texas Instruments Inc. + * + * Author: Graeme Gregory + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct resource gpadc_resource[] = { + { + .name = "EOC_SW", + .start = PALMAS_GPADC_EOC_SW_IRQ, + .end = PALMAS_GPADC_EOC_SW_IRQ, + .flags = IORESOURCE_IRQ, + } +}; + +static const struct resource usb_resource[] = { + { + .name = "ID", + .start = PALMAS_ID_OTG_IRQ, + .end = PALMAS_ID_OTG_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "ID_WAKEUP", + .start = PALMAS_ID_IRQ, + .end = PALMAS_ID_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "VBUS", + .start = PALMAS_VBUS_OTG_IRQ, + .end = PALMAS_VBUS_OTG_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "VBUS_WAKEUP", + .start = PALMAS_VBUS_IRQ, + .end = PALMAS_VBUS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct resource rtc_resource[] = { + { + .name = "RTC_ALARM", + .start = PALMAS_RTC_ALARM_IRQ, + .end = PALMAS_RTC_ALARM_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct resource pwron_resource[] = { + { + .name = "PWRON_BUTTON", + .start = PALMAS_PWRON_IRQ, + .end = PALMAS_PWRON_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +enum palmas_ids { + PALMAS_PMIC_ID, + PALMAS_GPIO_ID, + PALMAS_LEDS_ID, + PALMAS_WDT_ID, + PALMAS_RTC_ID, + PALMAS_PWRBUTTON_ID, + PALMAS_GPADC_ID, + PALMAS_RESOURCE_ID, + PALMAS_CLK_ID, + PALMAS_PWM_ID, + PALMAS_USB_ID, +}; + +static const struct mfd_cell palmas_children[] = { + { + .name = "palmas-pmic", + .id = PALMAS_PMIC_ID, + }, + { + .name = "palmas-gpio", + .id = PALMAS_GPIO_ID, + }, + { + .name = "palmas-leds", + .id = PALMAS_LEDS_ID, + }, + { + .name = "palmas-wdt", + .id = PALMAS_WDT_ID, + }, + { + .name = "palmas-rtc", + .num_resources = ARRAY_SIZE(rtc_resource), + .resources = rtc_resource, + .id = PALMAS_RTC_ID, + }, + { + .name = "palmas-pwrbutton", + .num_resources = ARRAY_SIZE(pwron_resource), + .resources = pwron_resource, + .id = PALMAS_PWRBUTTON_ID, + }, + { + .name = "palmas-gpadc", + .num_resources = ARRAY_SIZE(gpadc_resource), + .resources = gpadc_resource, + .id = PALMAS_GPADC_ID, + }, + { + .name = "palmas-resource", + .id = PALMAS_RESOURCE_ID, + }, + { + .name = "palmas-clk", + .id = PALMAS_CLK_ID, + }, + { + .name = "palmas-pwm", + .id = PALMAS_PWM_ID, + }, + { + .name = "palmas-usb", + .num_resources = ARRAY_SIZE(usb_resource), + .resources = usb_resource, + .id = PALMAS_USB_ID, + } +}; + +static const struct regmap_config palmas_regmap_config[PALMAS_NUM_CLIENTS] = { + { + .reg_bits = 8, + .val_bits = 8, + .max_register = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE, + PALMAS_PRIMARY_SECONDARY_PAD3), + }, + { + .reg_bits = 8, + .val_bits = 8, + .max_register = PALMAS_BASE_TO_REG(PALMAS_GPADC_BASE, + PALMAS_GPADC_SMPS_VSEL_MONITORING), + }, + { + .reg_bits = 8, + .val_bits = 8, + .max_register = PALMAS_BASE_TO_REG(PALMAS_TRIM_GPADC_BASE, + PALMAS_GPADC_TRIM16), + }, +}; + +static const struct regmap_irq palmas_irqs[] = { + /* INT1 IRQs */ + [PALMAS_CHARG_DET_N_VBUS_OVV_IRQ] = { + .mask = PALMAS_INT1_STATUS_CHARG_DET_N_VBUS_OVV, + }, + [PALMAS_PWRON_IRQ] = { + .mask = PALMAS_INT1_STATUS_PWRON, + }, + [PALMAS_LONG_PRESS_KEY_IRQ] = { + .mask = PALMAS_INT1_STATUS_LONG_PRESS_KEY, + }, + [PALMAS_RPWRON_IRQ] = { + .mask = PALMAS_INT1_STATUS_RPWRON, + }, + [PALMAS_PWRDOWN_IRQ] = { + .mask = PALMAS_INT1_STATUS_PWRDOWN, + }, + [PALMAS_HOTDIE_IRQ] = { + .mask = PALMAS_INT1_STATUS_HOTDIE, + }, + [PALMAS_VSYS_MON_IRQ] = { + .mask = PALMAS_INT1_STATUS_VSYS_MON, + }, + [PALMAS_VBAT_MON_IRQ] = { + .mask = PALMAS_INT1_STATUS_VBAT_MON, + }, + /* INT2 IRQs*/ + [PALMAS_RTC_ALARM_IRQ] = { + .mask = PALMAS_INT2_STATUS_RTC_ALARM, + .reg_offset = 1, + }, + [PALMAS_RTC_TIMER_IRQ] = { + .mask = PALMAS_INT2_STATUS_RTC_TIMER, + .reg_offset = 1, + }, + [PALMAS_WDT_IRQ] = { + .mask = PALMAS_INT2_STATUS_WDT, + .reg_offset = 1, + }, + [PALMAS_BATREMOVAL_IRQ] = { + .mask = PALMAS_INT2_STATUS_BATREMOVAL, + .reg_offset = 1, + }, + [PALMAS_RESET_IN_IRQ] = { + .mask = PALMAS_INT2_STATUS_RESET_IN, + .reg_offset = 1, + }, + [PALMAS_FBI_BB_IRQ] = { + .mask = PALMAS_INT2_STATUS_FBI_BB, + .reg_offset = 1, + }, + [PALMAS_SHORT_IRQ] = { + .mask = PALMAS_INT2_STATUS_SHORT, + .reg_offset = 1, + }, + [PALMAS_VAC_ACOK_IRQ] = { + .mask = PALMAS_INT2_STATUS_VAC_ACOK, + .reg_offset = 1, + }, + /* INT3 IRQs */ + [PALMAS_GPADC_AUTO_0_IRQ] = { + .mask = PALMAS_INT3_STATUS_GPADC_AUTO_0, + .reg_offset = 2, + }, + [PALMAS_GPADC_AUTO_1_IRQ] = { + .mask = PALMAS_INT3_STATUS_GPADC_AUTO_1, + .reg_offset = 2, + }, + [PALMAS_GPADC_EOC_SW_IRQ] = { + .mask = PALMAS_INT3_STATUS_GPADC_EOC_SW, + .reg_offset = 2, + }, + [PALMAS_GPADC_EOC_RT_IRQ] = { + .mask = PALMAS_INT3_STATUS_GPADC_EOC_RT, + .reg_offset = 2, + }, + [PALMAS_ID_OTG_IRQ] = { + .mask = PALMAS_INT3_STATUS_ID_OTG, + .reg_offset = 2, + }, + [PALMAS_ID_IRQ] = { + .mask = PALMAS_INT3_STATUS_ID, + .reg_offset = 2, + }, + [PALMAS_VBUS_OTG_IRQ] = { + .mask = PALMAS_INT3_STATUS_VBUS_OTG, + .reg_offset = 2, + }, + [PALMAS_VBUS_IRQ] = { + .mask = PALMAS_INT3_STATUS_VBUS, + .reg_offset = 2, + }, + /* INT4 IRQs */ + [PALMAS_GPIO_0_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_0, + .reg_offset = 3, + }, + [PALMAS_GPIO_1_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_1, + .reg_offset = 3, + }, + [PALMAS_GPIO_2_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_2, + .reg_offset = 3, + }, + [PALMAS_GPIO_3_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_3, + .reg_offset = 3, + }, + [PALMAS_GPIO_4_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_4, + .reg_offset = 3, + }, + [PALMAS_GPIO_5_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_5, + .reg_offset = 3, + }, + [PALMAS_GPIO_6_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_6, + .reg_offset = 3, + }, + [PALMAS_GPIO_7_IRQ] = { + .mask = PALMAS_INT4_STATUS_GPIO_7, + .reg_offset = 3, + }, +}; + +static struct regmap_irq_chip palmas_irq_chip = { + .name = "palmas", + .irqs = palmas_irqs, + .num_irqs = ARRAY_SIZE(palmas_irqs), + + .num_regs = 4, + .irq_reg_stride = 5, + .status_base = PALMAS_BASE_TO_REG(PALMAS_INTERRUPT_BASE, + PALMAS_INT1_STATUS), + .mask_base = PALMAS_BASE_TO_REG(PALMAS_INTERRUPT_BASE, + PALMAS_INT1_MASK), +}; + +static int __devinit palmas_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct palmas *palmas; + struct palmas_platform_data *pdata; + int ret = 0, i; + unsigned int reg, addr; + int slave; + struct mfd_cell *children; + + pdata = dev_get_platdata(&i2c->dev); + if (!pdata) + return -EINVAL; + + palmas = devm_kzalloc(&i2c->dev, sizeof(struct palmas), GFP_KERNEL); + if (palmas == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, palmas); + palmas->dev = &i2c->dev; + palmas->id = id->driver_data; + palmas->irq = i2c->irq; + + for (i = 0; i < PALMAS_NUM_CLIENTS; i++) { + if (i == 0) + palmas->i2c_clients[i] = i2c; + else { + palmas->i2c_clients[i] = + i2c_new_dummy(i2c->adapter, + i2c->addr + i); + if (!palmas->i2c_clients[i]) { + dev_err(palmas->dev, + "can't attach client %d\n", i); + ret = -ENOMEM; + goto err; + } + } + palmas->regmap[i] = devm_regmap_init_i2c(palmas->i2c_clients[i], + &palmas_regmap_config[i]); + if (IS_ERR(palmas->regmap[i])) { + ret = PTR_ERR(palmas->regmap[i]); + dev_err(palmas->dev, + "Failed to allocate regmap %d, err: %d\n", + i, ret); + goto err; + } + } + + ret = regmap_add_irq_chip(palmas->regmap[1], palmas->irq, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, -1, &palmas_irq_chip, + &palmas->irq_data); + if (ret < 0) + goto err; + + slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE); + addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE, + PALMAS_PRIMARY_SECONDARY_PAD1); + + if (pdata->mux_from_pdata) { + reg = pdata->pad1; + ret = regmap_write(palmas->regmap[slave], addr, reg); + if (ret) + goto err; + } else { + ret = regmap_read(palmas->regmap[slave], addr, ®); + if (ret) + goto err; + } + + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_0)) + palmas->gpio_muxed |= PALMAS_GPIO_0_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK)) + palmas->gpio_muxed |= PALMAS_GPIO_1_MUXED; + else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK) == + (2 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT)) + palmas->led_muxed |= PALMAS_LED1_MUXED; + else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK) == + (3 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT)) + palmas->pwm_muxed |= PALMAS_PWM1_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK)) + palmas->gpio_muxed |= PALMAS_GPIO_2_MUXED; + else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK) == + (2 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_SHIFT)) + palmas->led_muxed |= PALMAS_LED2_MUXED; + else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK) == + (3 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_SHIFT)) + palmas->pwm_muxed |= PALMAS_PWM2_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_3)) + palmas->gpio_muxed |= PALMAS_GPIO_3_MUXED; + + addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE, + PALMAS_PRIMARY_SECONDARY_PAD2); + + if (pdata->mux_from_pdata) { + reg = pdata->pad2; + ret = regmap_write(palmas->regmap[slave], addr, reg); + if (ret) + goto err; + } else { + ret = regmap_read(palmas->regmap[slave], addr, ®); + if (ret) + goto err; + } + + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_4)) + palmas->gpio_muxed |= PALMAS_GPIO_4_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_5_MASK)) + palmas->gpio_muxed |= PALMAS_GPIO_5_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_6)) + palmas->gpio_muxed |= PALMAS_GPIO_6_MUXED; + if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_7_MASK)) + palmas->gpio_muxed |= PALMAS_GPIO_7_MUXED; + + dev_info(palmas->dev, "Muxing GPIO %x, PWM %x, LED %x\n", + palmas->gpio_muxed, palmas->pwm_muxed, + palmas->led_muxed); + + reg = pdata->power_ctrl; + + slave = PALMAS_BASE_TO_SLAVE(PALMAS_PMU_CONTROL_BASE); + addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE, PALMAS_POWER_CTRL); + + ret = regmap_write(palmas->regmap[slave], addr, reg); + if (ret) + goto err; + + children = kmemdup(palmas_children, sizeof(palmas_children), + GFP_KERNEL); + if (!children) { + ret = -ENOMEM; + goto err; + } + + ret = mfd_add_devices(palmas->dev, -1, + children, ARRAY_SIZE(palmas_children), + NULL, regmap_irq_chip_get_base(palmas->irq_data)); + kfree(children); + + if (ret < 0) + goto err; + + return ret; + +err: + mfd_remove_devices(palmas->dev); + kfree(palmas); + return ret; +} + +static int palmas_i2c_remove(struct i2c_client *i2c) +{ + struct palmas *palmas = i2c_get_clientdata(i2c); + + mfd_remove_devices(palmas->dev); + regmap_del_irq_chip(palmas->irq, palmas->irq_data); + + return 0; +} + +static const struct i2c_device_id palmas_i2c_id[] = { + { "palmas", }, + { "twl6035", }, + { "twl6037", }, + { "tps65913", }, +}; +MODULE_DEVICE_TABLE(i2c, palmas_i2c_id); + +static struct of_device_id __devinitdata of_palmas_match_tbl[] = { + { .compatible = "ti,palmas", }, + { /* end */ } +}; + +static struct i2c_driver palmas_i2c_driver = { + .driver = { + .name = "palmas", + .of_match_table = of_palmas_match_tbl, + .owner = THIS_MODULE, + }, + .probe = palmas_i2c_probe, + .remove = palmas_i2c_remove, + .id_table = palmas_i2c_id, +}; + +static int __init palmas_i2c_init(void) +{ + return i2c_add_driver(&palmas_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(palmas_i2c_init); + +static void __exit palmas_i2c_exit(void) +{ + i2c_del_driver(&palmas_i2c_driver); +} +module_exit(palmas_i2c_exit); + +MODULE_AUTHOR("Graeme Gregory "); +MODULE_DESCRIPTION("Palmas chip family multi-function driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From c948ef3ae71c18c1079333b65d6887ceb4577618 Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Tue, 15 May 2012 15:48:57 +0900 Subject: mfd: palmas PMIC device support Kconfig Add the new palmas MFD to Kconfig and Makefile Signed-off-by: Graeme Gregory Signed-off-by: Mark Brown --- drivers/mfd/Kconfig | 10 ++++++++++ drivers/mfd/Makefile | 2 ++ 2 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 697d686927f8..e35cb6bc9003 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -891,6 +891,16 @@ config MFD_ANATOP MFD controller. This controller embeds regulator and thermal devices for Freescale i.MX platforms. +config MFD_PALMAS + bool "Support for the TI Palmas series chips" + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + depends on I2C=y + help + If you say yes here you get support for the Palmas + series of PMIC chips from Texas Instruments. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 05fa538c5efe..77293e150239 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -113,6 +113,8 @@ obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_TPS65090) += tps65090.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o +obj-$(CONFIG_MFD_PALMAS) += palmas.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o +obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o -- cgit v1.2.3