diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/regmap/Kconfig | 3 | ||||
-rw-r--r-- | drivers/base/regmap/Makefile | 1 | ||||
-rw-r--r-- | drivers/base/regmap/internal.h | 26 | ||||
-rw-r--r-- | drivers/base/regmap/regcache-lzo.c | 11 | ||||
-rw-r--r-- | drivers/base/regmap/regcache-rbtree.c | 44 | ||||
-rw-r--r-- | drivers/base/regmap/regcache.c | 34 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 18 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-i2c.c | 13 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-irq.c | 184 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-mmio.c | 224 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-spi.c | 13 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 276 |
12 files changed, 677 insertions, 170 deletions
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 0f6c7fb418e..9ef0a5326f1 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 defd57963c8..5e75d1b683e 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/internal.h b/drivers/base/regmap/internal.h index fcafc5b2e65..b986b8660b0 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -26,21 +26,30 @@ 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); }; +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 */ struct regmap_format format; /* Buffer format */ const struct regmap_bus *bus; + void *bus_context; + const char *name; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; + const char *debugfs_name; #endif unsigned int max_register; @@ -52,6 +61,10 @@ struct regmap { u8 read_flag_mask; u8 write_flag_mask; + /* 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; enum regcache_type cache_type; @@ -79,6 +92,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 { @@ -101,11 +117,11 @@ 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) { } -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 diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 483b06d4a38..afd6aa91a0d 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 92b779ee002..e6732cf7c06 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,19 +141,20 @@ 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; - mutex_lock(&map->lock); + map->lock(map); for (node = rb_first(&rbtree_ctx->root); node != NULL; 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) @@ -161,7 +165,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) seq_printf(s, "%d nodes, %d registers, average %d registers\n", nodes, registers, average); - mutex_unlock(&map->lock); + map->unlock(map); return 0; } @@ -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 74b69095def..835883bda97 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; @@ -264,7 +268,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", @@ -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", @@ -296,7 +304,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 +331,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 +350,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; } @@ -362,11 +370,11 @@ EXPORT_SYMBOL_GPL(regcache_sync_region); */ 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); @@ -381,9 +389,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); @@ -400,11 +408,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-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 251eb70f83e..bb1ff175b96 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; @@ -242,10 +242,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; @@ -274,6 +281,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-i2c.c b/drivers/base/regmap/regmap-i2c.c index 9a3a8c56438..5f6b2478bf1 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -15,8 +15,9 @@ #include <linux/module.h> #include <linux/init.h> -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-irq.c b/drivers/base/regmap/regmap-irq.c index 1befaa7a31c..4fac4b9be88 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -15,6 +15,7 @@ #include <linux/regmap.h> #include <linux/irq.h> #include <linux/interrupt.h> +#include <linux/irqdomain.h> #include <linux/slab.h> #include "internal.h" @@ -26,18 +27,20 @@ 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; unsigned int *mask_buf; unsigned int *mask_buf_def; + + unsigned int irq_reg_stride; }; 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) @@ -50,6 +53,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; /* @@ -58,11 +62,13 @@ 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->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", - d->chip->mask_base + i); + d->chip->mask_base + (i * map->reg_stride)); } mutex_unlock(&d->lock); @@ -71,17 +77,19 @@ 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); - const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); + struct regmap *map = d->map; + const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); - 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) { 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); + struct regmap *map = d->map; + const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); - 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 = { @@ -98,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 @@ -118,36 +116,34 @@ 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->status_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; } 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->irq_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) { - handle_nested_irq(data->irq_base + i); + if (data->status_buf[chip->irqs[i].reg_offset / + map->reg_stride] & chip->irqs[i].mask) { + handle_nested_irq(irq_find_mapping(data->domain, i)); handled = true; } } @@ -158,6 +154,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 * @@ -178,30 +199,37 @@ 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; - 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; + 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; + } + + 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); if (!d) return -ENOMEM; + *data = d; + d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, GFP_KERNEL); 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) @@ -215,54 +243,59 @@ 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++) - 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->irq_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; } } - /* 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); - kfree(d->status_reg_buf); kfree(d->status_buf); kfree(d); return ret; @@ -281,9 +314,9 @@ 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); kfree(d->status_buf); kfree(d); } @@ -298,6 +331,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/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c new file mode 100644 index 00000000000..febd6de6c8a --- /dev/null +++ b/drivers/base/regmap/regmap-mmio.c @@ -0,0 +1,224 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +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; + + BUG_ON(reg_size != 4); + + 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 */ + BUG(); + } + 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) +{ + BUG_ON(count < 4); + + 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; + + BUG_ON(reg_size != 4); + + 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 */ + BUG(); + } + 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; + int min_stride; + + if (config->reg_bits != 32) + return ERR_PTR(-EINVAL); + + if (config->pad_bits) + return ERR_PTR(-EINVAL); + + 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); + + ctx->regs = regs; + ctx->val_bytes = config->val_bits / 8; + + return ctx; +} + +/** + * regmap_init_mmio(): Initialise register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +struct regmap *regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + ctx = regmap_mmio_gen_context(regs, config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return regmap_init(dev, ®map_mmio, ctx, config); +} +EXPORT_SYMBOL_GPL(regmap_init_mmio); + +/** + * devm_regmap_init_mmio(): Initialise managed register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + ctx = regmap_mmio_gen_context(regs, config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return devm_regmap_init(dev, ®map_mmio, ctx, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_mmio); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 7c0c35a39c3..ffa46a92ad3 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -15,17 +15,19 @@ #include <linux/init.h> #include <linux/module.h> -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 bb80853ff27..0bcda488f11 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -112,25 +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_32(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, 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) @@ -149,6 +160,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; @@ -158,11 +179,41 @@ 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); +} + +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 * * @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,9 +222,10 @@ 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; + struct regmap *map, **m; int ret = -EINVAL; if (!bus || !config) @@ -185,20 +237,36 @@ 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; 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->use_single_rw = config->use_single_rw; 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; 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; @@ -207,7 +275,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: @@ -273,12 +341,19 @@ 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; 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; @@ -289,14 +364,25 @@ 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) 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: @@ -316,6 +402,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 +412,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 +421,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); @@ -360,7 +448,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); @@ -372,14 +460,14 @@ 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; ret = regcache_init(map, config); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -391,11 +479,51 @@ 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); } 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) { @@ -408,7 +536,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) { @@ -417,7 +546,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", @@ -431,7 +561,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; @@ -444,12 +574,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 +594,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 +628,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); @@ -506,7 +636,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 + @@ -529,11 +659,14 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; - mutex_lock(&map->lock); + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map); ret = _regmap_write(map, reg, val); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -560,11 +693,16 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, { int ret; - mutex_lock(&map->lock); + if (val_len % map->format.val_bytes) + return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map); ret = _regmap_raw_write(map, reg, val, val_len); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -593,8 +731,10 @@ 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; - mutex_lock(&map->lock); + map->lock(map); /* No formatting is require if val_byte is 1 */ if (val_bytes == 1) { @@ -609,13 +749,28 @@ 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); out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_write); @@ -626,7 +781,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 @@ -639,7 +794,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); @@ -672,6 +827,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; } @@ -689,11 +847,14 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; - mutex_lock(&map->lock); + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map); ret = _regmap_read(map, reg, val); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -718,7 +879,12 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int v; int ret, i; - mutex_lock(&map->lock); + if (val_len % map->format.val_bytes) + return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map); if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || map->cache_type == REGCACHE_NONE) { @@ -730,16 +896,17 @@ 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; - map->format.format_val(val + (i * val_bytes), v); + map->format.format_val(val + (i * val_bytes), v, 0); } } out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -765,18 +932,37 @@ 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); - 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); } else { for (i = 0; i < val_count; i++) { unsigned int ival; - ret = regmap_read(map, reg + i, &ival); + ret = regmap_read(map, reg + (i * map->reg_stride), + &ival); if (ret != 0) return ret; memcpy(val + (i * val_bytes), &ival, val_bytes); @@ -794,7 +980,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) @@ -811,7 +997,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, } out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -878,7 +1064,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; @@ -906,7 +1092,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; } |