diff options
Diffstat (limited to 'drivers/base/regmap')
| -rw-r--r-- | drivers/base/regmap/Kconfig | 5 | ||||
| -rw-r--r-- | drivers/base/regmap/Makefile | 1 | ||||
| -rw-r--r-- | drivers/base/regmap/internal.h | 10 | ||||
| -rw-r--r-- | drivers/base/regmap/regcache-rbtree.c | 8 | ||||
| -rw-r--r-- | drivers/base/regmap/regcache.c | 32 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 59 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap-i2c.c | 105 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap-irq.c | 35 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap-mmio.c | 95 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap-spi.c | 4 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap-spmi.c | 256 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap.c | 815 | 
12 files changed, 1265 insertions, 160 deletions
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index f0d30543fcc..4251570610c 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -3,7 +3,7 @@  # subsystems should select the appropriate symbols.  config REGMAP -	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_MMIO || REGMAP_IRQ) +	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ)  	select LZO_COMPRESS  	select LZO_DECOMPRESS  	select IRQ_DOMAIN if REGMAP_IRQ @@ -15,6 +15,9 @@ config REGMAP_I2C  config REGMAP_SPI  	tristate +config REGMAP_SPMI +	tristate +  config REGMAP_MMIO  	tristate diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index cf129980abd..a7c670b4123 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.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_SPMI) += regmap-spmi.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 57f777835d9..7d1326985be 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -44,7 +44,6 @@ struct regmap_format {  struct regmap_async {  	struct list_head list; -	struct work_struct cleanup;  	struct regmap *map;  	void *work_buf;  }; @@ -64,9 +63,11 @@ struct regmap {  	void *bus_context;  	const char *name; +	bool async;  	spinlock_t async_lock;  	wait_queue_head_t async_waitq;  	struct list_head async_list; +	struct list_head async_free;  	int async_ret;  #ifdef CONFIG_DEBUG_FS @@ -133,6 +134,8 @@ struct regmap {  	/* if set, converts bulk rw to single rw */  	bool use_single_rw; +	/* if set, the device supports multi write mode */ +	bool can_multi_write;  	struct rb_root range_tree;  	void *selector_work_buf;	/* Scratch buffer used for selector */ @@ -179,6 +182,9 @@ struct regmap_field {  	/* lsb */  	unsigned int shift;  	unsigned int reg; + +	unsigned int id_size; +	unsigned int id_offset;  };  #ifdef CONFIG_DEBUG_FS @@ -218,7 +224,7 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,  int regcache_lookup_reg(struct regmap *map, unsigned int reg);  int _regmap_raw_write(struct regmap *map, unsigned int reg, -		      const void *val, size_t val_len, bool async); +		      const void *val, size_t val_len);  void regmap_async_complete_cb(struct regmap_async *async, int ret); diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 930cad4e5df..6a7e4fa1285 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -23,16 +23,16 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,  static int regcache_rbtree_exit(struct regmap *map);  struct regcache_rbtree_node { -	/* the actual rbtree node holding this block */ -	struct rb_node node; -	/* base register handled by this block */ -	unsigned int base_reg;  	/* block of adjacent registers */  	void *block;  	/* Which registers are present */  	long *cache_present; +	/* base register handled by this block */ +	unsigned int base_reg;  	/* number of registers available in the block */  	unsigned int blklen; +	/* the actual rbtree node holding this block */ +	struct rb_node node;  } __attribute__ ((packed));  struct regcache_rbtree_ctx { diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index d6c2d691b6e..29b4128da0b 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -249,11 +249,12 @@ static int regcache_default_sync(struct regmap *map, unsigned int min,  {  	unsigned int reg; -	for (reg = min; reg <= max; reg++) { +	for (reg = min; reg <= max; reg += map->reg_stride) {  		unsigned int val;  		int ret; -		if (regmap_volatile(map, reg)) +		if (regmap_volatile(map, reg) || +		    !regmap_writeable(map, reg))  			continue;  		ret = regcache_read(map, reg, &val); @@ -307,13 +308,11 @@ int regcache_sync(struct regmap *map)  	if (!map->cache_dirty)  		goto out; +	map->async = true; +  	/* 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", @@ -332,11 +331,15 @@ int regcache_sync(struct regmap *map)  		map->cache_dirty = false;  out: -	trace_regcache_sync(map->dev, name, "stop");  	/* Restore the bypass state */ +	map->async = false;  	map->cache_bypass = bypass;  	map->unlock(map->lock_arg); +	regmap_async_complete(map); + +	trace_regcache_sync(map->dev, name, "stop"); +  	return ret;  }  EXPORT_SYMBOL_GPL(regcache_sync); @@ -375,17 +378,23 @@ int regcache_sync_region(struct regmap *map, unsigned int min,  	if (!map->cache_dirty)  		goto out; +	map->async = true; +  	if (map->cache_ops->sync)  		ret = map->cache_ops->sync(map, min, max);  	else  		ret = regcache_default_sync(map, min, max);  out: -	trace_regcache_sync(map->dev, name, "stop region");  	/* Restore the bypass state */  	map->cache_bypass = bypass; +	map->async = false;  	map->unlock(map->lock_arg); +	regmap_async_complete(map); + +	trace_regcache_sync(map->dev, name, "stop region"); +  	return ret;  }  EXPORT_SYMBOL_GPL(regcache_sync_region); @@ -624,15 +633,14 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,  	if (*data == NULL)  		return 0; -	count = cur - base; +	count = (cur - base) / map->reg_stride;  	dev_dbg(map->dev, "Writing %zu bytes for %d registers from 0x%x-0x%x\n", -		count * val_bytes, count, base, cur - 1); +		count * val_bytes, count, base, cur - map->reg_stride);  	map->cache_bypass = 1; -	ret = _regmap_raw_write(map, base, *data, count * val_bytes, -				false); +	ret = _regmap_raw_write(map, base, *data, count * val_bytes);  	map->cache_bypass = 0; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index de11ecaf383..45d812c0ea7 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -15,10 +15,19 @@  #include <linux/debugfs.h>  #include <linux/uaccess.h>  #include <linux/device.h> +#include <linux/list.h>  #include "internal.h" +struct regmap_debugfs_node { +	struct regmap *map; +	const char *name; +	struct list_head link; +}; +  static struct dentry *regmap_debugfs_root; +static LIST_HEAD(regmap_debugfs_early_list); +static DEFINE_MUTEX(regmap_debugfs_early_lock);  /* Calculate the length of a fixed format  */  static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size) @@ -465,6 +474,20 @@ void regmap_debugfs_init(struct regmap *map, const char *name)  	struct rb_node *next;  	struct regmap_range_node *range_node; +	/* If we don't have the debugfs root yet, postpone init */ +	if (!regmap_debugfs_root) { +		struct regmap_debugfs_node *node; +		node = kzalloc(sizeof(*node), GFP_KERNEL); +		if (!node) +			return; +		node->map = map; +		node->name = name; +		mutex_lock(®map_debugfs_early_lock); +		list_add(&node->link, ®map_debugfs_early_list); +		mutex_unlock(®map_debugfs_early_lock); +		return; +	} +  	INIT_LIST_HEAD(&map->debugfs_off_cache);  	mutex_init(&map->cache_lock); @@ -488,7 +511,7 @@ void regmap_debugfs_init(struct regmap *map, const char *name)  	debugfs_create_file("range", 0400, map->debugfs,  			    map, ®map_reg_ranges_fops); -	if (map->max_register) { +	if (map->max_register || regmap_readable(map, 0)) {  		debugfs_create_file("registers", 0400, map->debugfs,  				    map, ®map_map_fops);  		debugfs_create_file("access", 0400, map->debugfs, @@ -519,18 +542,42 @@ void regmap_debugfs_init(struct regmap *map, const char *name)  void regmap_debugfs_exit(struct regmap *map)  { -	debugfs_remove_recursive(map->debugfs); -	mutex_lock(&map->cache_lock); -	regmap_debugfs_free_dump_cache(map); -	mutex_unlock(&map->cache_lock); -	kfree(map->debugfs_name); +	if (map->debugfs) { +		debugfs_remove_recursive(map->debugfs); +		mutex_lock(&map->cache_lock); +		regmap_debugfs_free_dump_cache(map); +		mutex_unlock(&map->cache_lock); +		kfree(map->debugfs_name); +	} else { +		struct regmap_debugfs_node *node, *tmp; + +		mutex_lock(®map_debugfs_early_lock); +		list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, +					 link) { +			if (node->map == map) { +				list_del(&node->link); +				kfree(node); +			} +		} +		mutex_unlock(®map_debugfs_early_lock); +	}  }  void regmap_debugfs_initcall(void)  { +	struct regmap_debugfs_node *node, *tmp; +  	regmap_debugfs_root = debugfs_create_dir("regmap", NULL);  	if (!regmap_debugfs_root) {  		pr_warn("regmap: Failed to create debugfs root\n");  		return;  	} + +	mutex_lock(®map_debugfs_early_lock); +	list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, link) { +		regmap_debugfs_init(node->map, node->name); +		list_del(&node->link); +		kfree(node); +	} +	mutex_unlock(®map_debugfs_early_lock);  } diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index fa6bf5279d2..ca193d1ef47 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -13,7 +13,79 @@  #include <linux/regmap.h>  #include <linux/i2c.h>  #include <linux/module.h> -#include <linux/init.h> + + +static int regmap_smbus_byte_reg_read(void *context, unsigned int reg, +				      unsigned int *val) +{ +	struct device *dev = context; +	struct i2c_client *i2c = to_i2c_client(dev); +	int ret; + +	if (reg > 0xff) +		return -EINVAL; + +	ret = i2c_smbus_read_byte_data(i2c, reg); +	if (ret < 0) +		return ret; + +	*val = ret; + +	return 0; +} + +static int regmap_smbus_byte_reg_write(void *context, unsigned int reg, +				       unsigned int val) +{ +	struct device *dev = context; +	struct i2c_client *i2c = to_i2c_client(dev); + +	if (val > 0xff || reg > 0xff) +		return -EINVAL; + +	return i2c_smbus_write_byte_data(i2c, reg, val); +} + +static struct regmap_bus regmap_smbus_byte = { +	.reg_write = regmap_smbus_byte_reg_write, +	.reg_read = regmap_smbus_byte_reg_read, +}; + +static int regmap_smbus_word_reg_read(void *context, unsigned int reg, +				      unsigned int *val) +{ +	struct device *dev = context; +	struct i2c_client *i2c = to_i2c_client(dev); +	int ret; + +	if (reg > 0xff) +		return -EINVAL; + +	ret = i2c_smbus_read_word_data(i2c, reg); +	if (ret < 0) +		return ret; + +	*val = ret; + +	return 0; +} + +static int regmap_smbus_word_reg_write(void *context, unsigned int reg, +				       unsigned int val) +{ +	struct device *dev = context; +	struct i2c_client *i2c = to_i2c_client(dev); + +	if (val > 0xffff || reg > 0xff) +		return -EINVAL; + +	return i2c_smbus_write_word_data(i2c, reg, val); +} + +static struct regmap_bus regmap_smbus_word = { +	.reg_write = regmap_smbus_word_reg_write, +	.reg_read = regmap_smbus_word_reg_read, +};  static int regmap_i2c_write(void *context, const void *data, size_t count)  { @@ -98,6 +170,23 @@ static struct regmap_bus regmap_i2c = {  	.read = regmap_i2c_read,  }; +static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, +					const struct regmap_config *config) +{ +	if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) +		return ®map_i2c; +	else if (config->val_bits == 16 && config->reg_bits == 8 && +		 i2c_check_functionality(i2c->adapter, +					 I2C_FUNC_SMBUS_WORD_DATA)) +		return ®map_smbus_word; +	else if (config->val_bits == 8 && config->reg_bits == 8 && +		 i2c_check_functionality(i2c->adapter, +					 I2C_FUNC_SMBUS_BYTE_DATA)) +		return ®map_smbus_byte; + +	return ERR_PTR(-ENOTSUPP); +} +  /**   * regmap_init_i2c(): Initialise register map   * @@ -110,7 +199,12 @@ 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, &i2c->dev, config); +	const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config); + +	if (IS_ERR(bus)) +		return ERR_CAST(bus); + +	return regmap_init(&i2c->dev, bus, &i2c->dev, config);  }  EXPORT_SYMBOL_GPL(regmap_init_i2c); @@ -127,7 +221,12 @@ 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, &i2c->dev, config); +	const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config); + +	if (IS_ERR(bus)) +		return ERR_CAST(bus); + +	return devm_regmap_init(&i2c->dev, bus, &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 d10456ffd81..6299a50a596 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -10,13 +10,13 @@   * published by the Free Software Foundation.   */ -#include <linux/export.h>  #include <linux/device.h> -#include <linux/regmap.h> -#include <linux/irq.h> +#include <linux/export.h>  #include <linux/interrupt.h> +#include <linux/irq.h>  #include <linux/irqdomain.h>  #include <linux/pm_runtime.h> +#include <linux/regmap.h>  #include <linux/slab.h>  #include "internal.h" @@ -105,6 +105,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  					"Failed to sync wakes in %x: %d\n",  					reg, ret);  		} + +		if (!d->chip->init_ack_masked) +			continue; +		/* +		 * Ack all the masked interrupts uncondictionly, +		 * OR if there is masked interrupt which hasn't been Acked, +		 * it'll be ignored in irq handler, then may introduce irq storm +		 */ +		if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) { +			reg = d->chip->ack_base + +				(i * map->reg_stride * d->irq_reg_stride); +			ret = regmap_write(map, reg, d->mask_buf[i]); +			if (ret != 0) +				dev_err(d->map->dev, "Failed to ack 0x%x: %d\n", +					reg, ret); +		}  	}  	if (d->chip->runtime_pm) @@ -255,7 +271,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)  	for (i = 0; i < data->chip->num_regs; i++) {  		data->status_buf[i] &= ~data->mask_buf[i]; -		if (data->status_buf[i] && chip->ack_base) { +		if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) {  			reg = chip->ack_base +  				(i * map->reg_stride * data->irq_reg_stride);  			ret = regmap_write(map, reg, data->status_buf[i]); @@ -331,6 +347,9 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  	int ret = -ENOMEM;  	u32 reg; +	if (chip->num_regs <= 0) +		return -EINVAL; +  	for (i = 0; i < chip->num_irqs; i++) {  		if (chip->irqs[i].reg_offset % map->reg_stride)  			return -EINVAL; @@ -352,8 +371,6 @@ 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) @@ -432,7 +449,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  			goto err_alloc;  		} -		if (d->status_buf[i] && chip->ack_base) { +		if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) {  			reg = chip->ack_base +  				(i * map->reg_stride * d->irq_reg_stride);  			ret = regmap_write(map, reg, @@ -490,6 +507,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  		goto err_domain;  	} +	*data = d; +  	return 0;  err_domain: @@ -517,7 +536,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... */ +	irq_domain_remove(d->domain);  	kfree(d->wake_buf);  	kfree(d->mask_buf_def);  	kfree(d->mask_buf); diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 98745dd77e8..04a329a377e 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -18,7 +18,6 @@  #include <linux/clk.h>  #include <linux/err.h> -#include <linux/init.h>  #include <linux/io.h>  #include <linux/module.h>  #include <linux/regmap.h> @@ -26,27 +25,83 @@  struct regmap_mmio_context {  	void __iomem *regs; +	unsigned reg_bytes;  	unsigned val_bytes; +	unsigned pad_bytes;  	struct clk *clk;  }; +static inline void regmap_mmio_regsize_check(size_t reg_size) +{ +	switch (reg_size) { +	case 1: +	case 2: +	case 4: +#ifdef CONFIG_64BIT +	case 8: +#endif +		break; +	default: +		BUG(); +	} +} + +static int regmap_mmio_regbits_check(size_t reg_bits) +{ +	switch (reg_bits) { +	case 8: +	case 16: +	case 32: +#ifdef CONFIG_64BIT +	case 64: +#endif +		return 0; +	default: +		return -EINVAL; +	} +} + +static inline void regmap_mmio_count_check(size_t count, u32 offset) +{ +	BUG_ON(count <= offset); +} + +static inline unsigned int +regmap_mmio_get_offset(const void *reg, size_t reg_size) +{ +	switch (reg_size) { +	case 1: +		return *(u8 *)reg; +	case 2: +		return *(u16 *)reg; +	case 4: +		return *(u32 *)reg; +#ifdef CONFIG_64BIT +	case 8: +		return *(u64 *)reg; +#endif +	default: +		BUG(); +	} +} +  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; +	unsigned int offset;  	int ret; -	BUG_ON(reg_size != 4); +	regmap_mmio_regsize_check(reg_size); -	if (ctx->clk) { +	if (!IS_ERR(ctx->clk)) {  		ret = clk_enable(ctx->clk);  		if (ret < 0)  			return ret;  	} -	offset = *(u32 *)reg; +	offset = regmap_mmio_get_offset(reg, reg_size);  	while (val_size) {  		switch (ctx->val_bytes) { @@ -73,7 +128,7 @@ static int regmap_mmio_gather_write(void *context,  		offset += ctx->val_bytes;  	} -	if (ctx->clk) +	if (!IS_ERR(ctx->clk))  		clk_disable(ctx->clk);  	return 0; @@ -81,9 +136,13 @@ static int regmap_mmio_gather_write(void *context,  static int regmap_mmio_write(void *context, const void *data, size_t count)  { -	BUG_ON(count < 4); +	struct regmap_mmio_context *ctx = context; +	unsigned int offset = ctx->reg_bytes + ctx->pad_bytes; -	return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4); +	regmap_mmio_count_check(count, offset); + +	return regmap_mmio_gather_write(context, data, ctx->reg_bytes, +					data + offset, count - offset);  }  static int regmap_mmio_read(void *context, @@ -91,18 +150,18 @@ static int regmap_mmio_read(void *context,  			    void *val, size_t val_size)  {  	struct regmap_mmio_context *ctx = context; -	u32 offset; +	unsigned int offset;  	int ret; -	BUG_ON(reg_size != 4); +	regmap_mmio_regsize_check(reg_size); -	if (ctx->clk) { +	if (!IS_ERR(ctx->clk)) {  		ret = clk_enable(ctx->clk);  		if (ret < 0)  			return ret;  	} -	offset = *(u32 *)reg; +	offset = regmap_mmio_get_offset(reg, reg_size);  	while (val_size) {  		switch (ctx->val_bytes) { @@ -129,7 +188,7 @@ static int regmap_mmio_read(void *context,  		offset += ctx->val_bytes;  	} -	if (ctx->clk) +	if (!IS_ERR(ctx->clk))  		clk_disable(ctx->clk);  	return 0; @@ -139,7 +198,7 @@ static void regmap_mmio_free_context(void *context)  {  	struct regmap_mmio_context *ctx = context; -	if (ctx->clk) { +	if (!IS_ERR(ctx->clk)) {  		clk_unprepare(ctx->clk);  		clk_put(ctx->clk);  	} @@ -165,8 +224,9 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,  	int min_stride;  	int ret; -	if (config->reg_bits != 32) -		return ERR_PTR(-EINVAL); +	ret = regmap_mmio_regbits_check(config->reg_bits); +	if (ret) +		return ERR_PTR(ret);  	if (config->pad_bits)  		return ERR_PTR(-EINVAL); @@ -209,6 +269,9 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,  	ctx->regs = regs;  	ctx->val_bytes = config->val_bits / 8; +	ctx->reg_bytes = config->reg_bits / 8; +	ctx->pad_bytes = config->pad_bits / 8; +	ctx->clk = ERR_PTR(-ENODEV);  	if (clk_id == NULL)  		return ctx; diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 4c506bd940f..0eb3097c0d7 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -12,7 +12,6 @@  #include <linux/regmap.h>  #include <linux/spi/spi.h> -#include <linux/init.h>  #include <linux/module.h>  #include "internal.h" @@ -73,7 +72,8 @@ static int regmap_spi_async_write(void *context,  	spi_message_init(&async->m);  	spi_message_add_tail(&async->t[0], &async->m); -	spi_message_add_tail(&async->t[1], &async->m); +	if (val) +		spi_message_add_tail(&async->t[1], &async->m);  	async->m.complete = regmap_spi_complete;  	async->m.context = async; diff --git a/drivers/base/regmap/regmap-spmi.c b/drivers/base/regmap/regmap-spmi.c new file mode 100644 index 00000000000..d7026dc3338 --- /dev/null +++ b/drivers/base/regmap/regmap-spmi.c @@ -0,0 +1,256 @@ +/* + * Register map access API - SPMI support + * + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * Based on regmap-i2c.c: + * Copyright 2011 Wolfson Microelectronics plc + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + * + */ +#include <linux/regmap.h> +#include <linux/spmi.h> +#include <linux/module.h> +#include <linux/init.h> + +static int regmap_spmi_base_read(void *context, +				 const void *reg, size_t reg_size, +				 void *val, size_t val_size) +{ +	u8 addr = *(u8 *)reg; +	int err = 0; + +	BUG_ON(reg_size != 1); + +	while (val_size-- && !err) +		err = spmi_register_read(context, addr++, val++); + +	return err; +} + +static int regmap_spmi_base_gather_write(void *context, +					 const void *reg, size_t reg_size, +					 const void *val, size_t val_size) +{ +	const u8 *data = val; +	u8 addr = *(u8 *)reg; +	int err = 0; + +	BUG_ON(reg_size != 1); + +	/* +	 * SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence, +	 * use it when possible. +	 */ +	if (addr == 0 && val_size) { +		err = spmi_register_zero_write(context, *data); +		if (err) +			goto err_out; + +		data++; +		addr++; +		val_size--; +	} + +	while (val_size) { +		err = spmi_register_write(context, addr, *data); +		if (err) +			goto err_out; + +		data++; +		addr++; +		val_size--; +	} + +err_out: +	return err; +} + +static int regmap_spmi_base_write(void *context, const void *data, +				  size_t count) +{ +	BUG_ON(count < 1); +	return regmap_spmi_base_gather_write(context, data, 1, data + 1, +					     count - 1); +} + +static struct regmap_bus regmap_spmi_base = { +	.read				= regmap_spmi_base_read, +	.write				= regmap_spmi_base_write, +	.gather_write			= regmap_spmi_base_gather_write, +	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE, +	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE, +}; + +/** + * regmap_init_spmi_base(): Create regmap for the Base register space + * @sdev:	SPMI device that will be interacted with + * @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_spmi_base(struct spmi_device *sdev, +				     const struct regmap_config *config) +{ +	return regmap_init(&sdev->dev, ®map_spmi_base, sdev, config); +} +EXPORT_SYMBOL_GPL(regmap_init_spmi_base); + +/** + * devm_regmap_init_spmi_base(): Create managed regmap for Base register space + * @sdev:	SPMI device that will be interacted with + * @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_spmi_base(struct spmi_device *sdev, +					  const struct regmap_config *config) +{ +	return devm_regmap_init(&sdev->dev, ®map_spmi_base, sdev, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_base); + +static int regmap_spmi_ext_read(void *context, +				const void *reg, size_t reg_size, +				void *val, size_t val_size) +{ +	int err = 0; +	size_t len; +	u16 addr; + +	BUG_ON(reg_size != 2); + +	addr = *(u16 *)reg; + +	/* +	 * Split accesses into two to take advantage of the more +	 * bandwidth-efficient 'Extended Register Read' command when possible +	 */ +	while (addr <= 0xFF && val_size) { +		len = min_t(size_t, val_size, 16); + +		err = spmi_ext_register_read(context, addr, val, len); +		if (err) +			goto err_out; + +		addr += len; +		val += len; +		val_size -= len; +	} + +	while (val_size) { +		len = min_t(size_t, val_size, 8); + +		err = spmi_ext_register_readl(context, addr, val, val_size); +		if (err) +			goto err_out; + +		addr += len; +		val += len; +		val_size -= len; +	} + +err_out: +	return err; +} + +static int regmap_spmi_ext_gather_write(void *context, +					const void *reg, size_t reg_size, +					const void *val, size_t val_size) +{ +	int err = 0; +	size_t len; +	u16 addr; + +	BUG_ON(reg_size != 2); + +	addr = *(u16 *)reg; + +	while (addr <= 0xFF && val_size) { +		len = min_t(size_t, val_size, 16); + +		err = spmi_ext_register_write(context, addr, val, len); +		if (err) +			goto err_out; + +		addr += len; +		val += len; +		val_size -= len; +	} + +	while (val_size) { +		len = min_t(size_t, val_size, 8); + +		err = spmi_ext_register_writel(context, addr, val, len); +		if (err) +			goto err_out; + +		addr += len; +		val += len; +		val_size -= len; +	} + +err_out: +	return err; +} + +static int regmap_spmi_ext_write(void *context, const void *data, +				 size_t count) +{ +	BUG_ON(count < 2); +	return regmap_spmi_ext_gather_write(context, data, 2, data + 2, +					    count - 2); +} + +static struct regmap_bus regmap_spmi_ext = { +	.read				= regmap_spmi_ext_read, +	.write				= regmap_spmi_ext_write, +	.gather_write			= regmap_spmi_ext_gather_write, +	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE, +	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE, +}; + +/** + * regmap_init_spmi_ext(): Create regmap for Ext register space + * @sdev:	Device that will be interacted with + * @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_spmi_ext(struct spmi_device *sdev, +				    const struct regmap_config *config) +{ +	return regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config); +} +EXPORT_SYMBOL_GPL(regmap_init_spmi_ext); + +/** + * devm_regmap_init_spmi_ext(): Create managed regmap for Ext register space + * @sdev:	SPMI device that will be interacted with + * @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_spmi_ext(struct spmi_device *sdev, +				     const struct regmap_config *config) +{ +	return devm_regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_ext); + +MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7d689a15c50..74d8c0672cf 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -35,22 +35,17 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,  			       unsigned int mask, unsigned int val,  			       bool *change); +static int _regmap_bus_reg_read(void *context, unsigned int reg, +				unsigned int *val);  static int _regmap_bus_read(void *context, unsigned int reg,  			    unsigned int *val);  static int _regmap_bus_formatted_write(void *context, unsigned int reg,  				       unsigned int val); +static int _regmap_bus_reg_write(void *context, unsigned int reg, +				 unsigned int val);  static int _regmap_bus_raw_write(void *context, unsigned int reg,  				 unsigned int val); -static void async_cleanup(struct work_struct *work) -{ -	struct regmap_async *async = container_of(work, struct regmap_async, -						  cleanup); - -	kfree(async->work_buf); -	kfree(async); -} -  bool regmap_reg_in_ranges(unsigned int reg,  			  const struct regmap_range *ranges,  			  unsigned int nranges) @@ -201,6 +196,13 @@ static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift)  	b[0] = cpu_to_be16(val << shift);  } +static void regmap_format_16_le(void *buf, unsigned int val, unsigned int shift) +{ +	__le16 *b = buf; + +	b[0] = cpu_to_le16(val << shift); +} +  static void regmap_format_16_native(void *buf, unsigned int val,  				    unsigned int shift)  { @@ -225,6 +227,13 @@ static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift)  	b[0] = cpu_to_be32(val << shift);  } +static void regmap_format_32_le(void *buf, unsigned int val, unsigned int shift) +{ +	__le32 *b = buf; + +	b[0] = cpu_to_le32(val << shift); +} +  static void regmap_format_32_native(void *buf, unsigned int val,  				    unsigned int shift)  { @@ -249,6 +258,13 @@ static unsigned int regmap_parse_16_be(const void *buf)  	return be16_to_cpu(b[0]);  } +static unsigned int regmap_parse_16_le(const void *buf) +{ +	const __le16 *b = buf; + +	return le16_to_cpu(b[0]); +} +  static void regmap_parse_16_be_inplace(void *buf)  {  	__be16 *b = buf; @@ -256,6 +272,13 @@ static void regmap_parse_16_be_inplace(void *buf)  	b[0] = be16_to_cpu(b[0]);  } +static void regmap_parse_16_le_inplace(void *buf) +{ +	__le16 *b = buf; + +	b[0] = le16_to_cpu(b[0]); +} +  static unsigned int regmap_parse_16_native(const void *buf)  {  	return *(u16 *)buf; @@ -278,6 +301,13 @@ static unsigned int regmap_parse_32_be(const void *buf)  	return be32_to_cpu(b[0]);  } +static unsigned int regmap_parse_32_le(const void *buf) +{ +	const __le32 *b = buf; + +	return le32_to_cpu(b[0]); +} +  static void regmap_parse_32_be_inplace(void *buf)  {  	__be32 *b = buf; @@ -285,6 +315,13 @@ static void regmap_parse_32_be_inplace(void *buf)  	b[0] = be32_to_cpu(b[0]);  } +static void regmap_parse_32_le_inplace(void *buf) +{ +	__le32 *b = buf; + +	b[0] = le32_to_cpu(b[0]); +} +  static unsigned int regmap_parse_32_native(const void *buf)  {  	return *(u32 *)buf; @@ -389,6 +426,28 @@ static void regmap_range_exit(struct regmap *map)  	kfree(map->selector_work_buf);  } +int regmap_attach_dev(struct device *dev, struct regmap *map, +		      const struct regmap_config *config) +{ +	struct regmap **m; + +	map->dev = dev; + +	regmap_debugfs_init(map, config->name); + +	/* Add a devres resource for dev_get_regmap() */ +	m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); +	if (!m) { +		regmap_debugfs_exit(map); +		return -ENOMEM; +	} +	*m = map; +	devres_add(dev, m); + +	return 0; +} +EXPORT_SYMBOL_GPL(regmap_attach_dev); +  /**   * regmap_init(): Initialise register map   * @@ -406,7 +465,7 @@ struct regmap *regmap_init(struct device *dev,  			   void *bus_context,  			   const struct regmap_config *config)  { -	struct regmap *map, **m; +	struct regmap *map;  	int ret = -EINVAL;  	enum regmap_endian reg_endian, val_endian;  	int i, j; @@ -448,6 +507,7 @@ struct regmap *regmap_init(struct device *dev,  	else  		map->reg_stride = 1;  	map->use_single_rw = config->use_single_rw; +	map->can_multi_write = config->can_multi_write;  	map->dev = dev;  	map->bus = bus;  	map->bus_context = bus_context; @@ -465,6 +525,7 @@ struct regmap *regmap_init(struct device *dev,  	spin_lock_init(&map->async_lock);  	INIT_LIST_HEAD(&map->async_list); +	INIT_LIST_HEAD(&map->async_free);  	init_waitqueue_head(&map->async_waitq);  	if (config->read_flag_mask || config->write_flag_mask) { @@ -480,6 +541,12 @@ struct regmap *regmap_init(struct device *dev,  		map->defer_caching = false;  		goto skip_format_initialization; +	} else if (!bus->read || !bus->write) { +		map->reg_read = _regmap_bus_reg_read; +		map->reg_write = _regmap_bus_reg_write; + +		map->defer_caching = false; +		goto skip_format_initialization;  	} else {  		map->reg_read  = _regmap_bus_read;  	} @@ -593,6 +660,11 @@ struct regmap *regmap_init(struct device *dev,  			map->format.parse_val = regmap_parse_16_be;  			map->format.parse_inplace = regmap_parse_16_be_inplace;  			break; +		case REGMAP_ENDIAN_LITTLE: +			map->format.format_val = regmap_format_16_le; +			map->format.parse_val = regmap_parse_16_le; +			map->format.parse_inplace = regmap_parse_16_le_inplace; +			break;  		case REGMAP_ENDIAN_NATIVE:  			map->format.format_val = regmap_format_16_native;  			map->format.parse_val = regmap_parse_16_native; @@ -614,6 +686,11 @@ struct regmap *regmap_init(struct device *dev,  			map->format.parse_val = regmap_parse_32_be;  			map->format.parse_inplace = regmap_parse_32_be_inplace;  			break; +		case REGMAP_ENDIAN_LITTLE: +			map->format.format_val = regmap_format_32_le; +			map->format.parse_val = regmap_parse_32_le; +			map->format.parse_inplace = regmap_parse_32_le_inplace; +			break;  		case REGMAP_ENDIAN_NATIVE:  			map->format.format_val = regmap_format_32_native;  			map->format.parse_val = regmap_parse_32_native; @@ -726,7 +803,7 @@ skip_format_initialization:  		new->window_start = range_cfg->window_start;  		new->window_len = range_cfg->window_len; -		if (_regmap_range_add(map, new) == false) { +		if (!_regmap_range_add(map, new)) {  			dev_err(map->dev, "Failed to add range %d\n", i);  			kfree(new);  			goto err_range; @@ -742,25 +819,19 @@ skip_format_initialization:  		}  	} -	regmap_debugfs_init(map, config->name); -  	ret = regcache_init(map, config);  	if (ret != 0)  		goto err_range; -	/* 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_debugfs; +	if (dev) { +		ret = regmap_attach_dev(dev, map, config); +		if (ret != 0) +			goto err_regcache;  	} -	*m = map; -	devres_add(dev, m);  	return map; -err_debugfs: -	regmap_debugfs_exit(map); +err_regcache:  	regcache_exit(map);  err_range:  	regmap_range_exit(map); @@ -821,6 +892,8 @@ static void regmap_field_init(struct regmap_field *rm_field,  	rm_field->reg = reg_field.reg;  	rm_field->shift = reg_field.lsb;  	rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb); +	rm_field->id_size = reg_field.id_size; +	rm_field->id_offset = reg_field.id_offset;  }  /** @@ -942,12 +1015,22 @@ EXPORT_SYMBOL_GPL(regmap_reinit_cache);   */  void regmap_exit(struct regmap *map)  { +	struct regmap_async *async; +  	regcache_exit(map);  	regmap_debugfs_exit(map);  	regmap_range_exit(map);  	if (map->bus && map->bus->free_context)  		map->bus->free_context(map->bus_context);  	kfree(map->work_buf); +	while (!list_empty(&map->async_free)) { +		async = list_first_entry_or_null(&map->async_free, +						 struct regmap_async, +						 list); +		list_del(&async->list); +		kfree(async->work_buf); +		kfree(async); +	}  	kfree(map);  }  EXPORT_SYMBOL_GPL(regmap_exit); @@ -1039,7 +1122,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,  }  int _regmap_raw_write(struct regmap *map, unsigned int reg, -		      const void *val, size_t val_len, bool async) +		      const void *val, size_t val_len)  {  	struct regmap_range_node *range;  	unsigned long flags; @@ -1091,7 +1174,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,  			dev_dbg(map->dev, "Writing window %d/%zu\n",  				win_residue, val_len / map->format.val_bytes);  			ret = _regmap_raw_write(map, reg, val, win_residue * -						map->format.val_bytes, async); +						map->format.val_bytes);  			if (ret != 0)  				return ret; @@ -1114,49 +1197,72 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,  	u8[0] |= map->write_flag_mask; -	if (async && map->bus->async_write) { -		struct regmap_async *async = map->bus->async_alloc(); -		if (!async) -			return -ENOMEM; +	/* +	 * Essentially all I/O mechanisms will be faster with a single +	 * buffer to write.  Since register syncs often generate raw +	 * writes of single registers optimise that case. +	 */ +	if (val != work_val && val_len == map->format.val_bytes) { +		memcpy(work_val, val, map->format.val_bytes); +		val = work_val; +	} + +	if (map->async && map->bus->async_write) { +		struct regmap_async *async;  		trace_regmap_async_write_start(map->dev, reg, val_len); -		async->work_buf = kzalloc(map->format.buf_size, -					  GFP_KERNEL | GFP_DMA); -		if (!async->work_buf) { -			kfree(async); -			return -ENOMEM; +		spin_lock_irqsave(&map->async_lock, flags); +		async = list_first_entry_or_null(&map->async_free, +						 struct regmap_async, +						 list); +		if (async) +			list_del(&async->list); +		spin_unlock_irqrestore(&map->async_lock, flags); + +		if (!async) { +			async = map->bus->async_alloc(); +			if (!async) +				return -ENOMEM; + +			async->work_buf = kzalloc(map->format.buf_size, +						  GFP_KERNEL | GFP_DMA); +			if (!async->work_buf) { +				kfree(async); +				return -ENOMEM; +			}  		} -		INIT_WORK(&async->cleanup, async_cleanup);  		async->map = map;  		/* If the caller supplied the value we can use it safely. */  		memcpy(async->work_buf, map->work_buf, map->format.pad_bytes +  		       map->format.reg_bytes + map->format.val_bytes); -		if (val == work_val) -			val = async->work_buf + map->format.pad_bytes + -				map->format.reg_bytes;  		spin_lock_irqsave(&map->async_lock, flags);  		list_add_tail(&async->list, &map->async_list);  		spin_unlock_irqrestore(&map->async_lock, flags); -		ret = map->bus->async_write(map->bus_context, async->work_buf, -					    map->format.reg_bytes + -					    map->format.pad_bytes, -					    val, val_len, async); +		if (val != work_val) +			ret = map->bus->async_write(map->bus_context, +						    async->work_buf, +						    map->format.reg_bytes + +						    map->format.pad_bytes, +						    val, val_len, async); +		else +			ret = map->bus->async_write(map->bus_context, +						    async->work_buf, +						    map->format.reg_bytes + +						    map->format.pad_bytes + +						    val_len, NULL, 0, async);  		if (ret != 0) {  			dev_err(map->dev, "Failed to schedule write: %d\n",  				ret);  			spin_lock_irqsave(&map->async_lock, flags); -			list_del(&async->list); +			list_move(&async->list, &map->async_free);  			spin_unlock_irqrestore(&map->async_lock, flags); - -			kfree(async->work_buf); -			kfree(async);  		}  		return ret; @@ -1240,6 +1346,14 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg,  	return ret;  } +static int _regmap_bus_reg_write(void *context, unsigned int reg, +				 unsigned int val) +{ +	struct regmap *map = context; + +	return map->bus->reg_write(map->bus_context, reg, val); +} +  static int _regmap_bus_raw_write(void *context, unsigned int reg,  				 unsigned int val)  { @@ -1253,7 +1367,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg,  				 map->work_buf +  				 map->format.reg_bytes +  				 map->format.pad_bytes, -				 map->format.val_bytes, false); +				 map->format.val_bytes);  }  static inline void *_regmap_map_get_context(struct regmap *map) @@ -1318,6 +1432,37 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)  EXPORT_SYMBOL_GPL(regmap_write);  /** + * regmap_write_async(): Write a value to a single register asynchronously + * + * @map: Register map to write to + * @reg: Register to write to + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val) +{ +	int ret; + +	if (reg % map->reg_stride) +		return -EINVAL; + +	map->lock(map->lock_arg); + +	map->async = true; + +	ret = _regmap_write(map, reg, val); + +	map->async = false; + +	map->unlock(map->lock_arg); + +	return ret; +} +EXPORT_SYMBOL_GPL(regmap_write_async); + +/**   * regmap_raw_write(): Write raw values to one or more registers   *   * @map: Register map to write to @@ -1345,7 +1490,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,  	map->lock(map->lock_arg); -	ret = _regmap_raw_write(map, reg, val, val_len, false); +	ret = _regmap_raw_write(map, reg, val, val_len);  	map->unlock(map->lock_arg); @@ -1369,6 +1514,74 @@ int regmap_field_write(struct regmap_field *field, unsigned int val)  }  EXPORT_SYMBOL_GPL(regmap_field_write); +/** + * regmap_field_update_bits():	Perform a read/modify/write cycle + *                              on the register field + * + * @field: Register field to write to + * @mask: Bitmask to change + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val) +{ +	mask = (mask << field->shift) & field->mask; + +	return regmap_update_bits(field->regmap, field->reg, +				  mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_field_update_bits); + +/** + * regmap_fields_write(): Write a value to a single register field with port ID + * + * @field: Register field to write to + * @id: port ID + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_write(struct regmap_field *field, unsigned int id, +			unsigned int val) +{ +	if (id >= field->id_size) +		return -EINVAL; + +	return regmap_update_bits(field->regmap, +				  field->reg + (field->id_offset * id), +				  field->mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_fields_write); + +/** + * regmap_fields_update_bits():	Perform a read/modify/write cycle + *                              on the register field + * + * @field: Register field to write to + * @id: port ID + * @mask: Bitmask to change + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_update_bits(struct regmap_field *field,  unsigned int id, +			      unsigned int mask, unsigned int val) +{ +	if (id >= field->id_size) +		return -EINVAL; + +	mask = (mask << field->shift) & field->mask; + +	return regmap_update_bits(field->regmap, +				  field->reg + (field->id_offset * id), +				  mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_fields_update_bits); +  /*   * regmap_bulk_write(): Write multiple registers to the device   * @@ -1388,56 +1601,316 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,  {  	int ret = 0, i;  	size_t val_bytes = map->format.val_bytes; -	void *wval; -	if (!map->bus) -		return -EINVAL; -	if (!map->format.parse_inplace) +	if (map->bus && !map->format.parse_inplace)  		return -EINVAL;  	if (reg % map->reg_stride)  		return -EINVAL; -	map->lock(map->lock_arg); +	/* +	 * Some devices don't support bulk write, for +	 * them we have a series of single write operations. +	 */ +	if (!map->bus || map->use_single_rw) { +		map->lock(map->lock_arg); +		for (i = 0; i < val_count; i++) { +			unsigned int ival; -	/* No formatting is require if val_byte is 1 */ -	if (val_bytes == 1) { -		wval = (void *)val; +			switch (val_bytes) { +			case 1: +				ival = *(u8 *)(val + (i * val_bytes)); +				break; +			case 2: +				ival = *(u16 *)(val + (i * val_bytes)); +				break; +			case 4: +				ival = *(u32 *)(val + (i * val_bytes)); +				break; +#ifdef CONFIG_64BIT +			case 8: +				ival = *(u64 *)(val + (i * val_bytes)); +				break; +#endif +			default: +				ret = -EINVAL; +				goto out; +			} + +			ret = _regmap_write(map, reg + (i * map->reg_stride), +					ival); +			if (ret != 0) +				goto out; +		} +out: +		map->unlock(map->lock_arg);  	} else { +		void *wval; +  		wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL);  		if (!wval) { -			ret = -ENOMEM;  			dev_err(map->dev, "Error in memory allocation\n"); -			goto out; +			return -ENOMEM;  		}  		for (i = 0; i < val_count * val_bytes; i += val_bytes)  			map->format.parse_inplace(wval + i); + +		map->lock(map->lock_arg); +		ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); +		map->unlock(map->lock_arg); + +		kfree(wval); +	} +	return ret; +} +EXPORT_SYMBOL_GPL(regmap_bulk_write); + +/* + * _regmap_raw_multi_reg_write() + * + * the (register,newvalue) pairs in regs have not been formatted, but + * they are all in the same page and have been changed to being page + * relative. The page register has been written if that was neccessary. + */ +static int _regmap_raw_multi_reg_write(struct regmap *map, +				       const struct reg_default *regs, +				       size_t num_regs) +{ +	int ret; +	void *buf; +	int i; +	u8 *u8; +	size_t val_bytes = map->format.val_bytes; +	size_t reg_bytes = map->format.reg_bytes; +	size_t pad_bytes = map->format.pad_bytes; +	size_t pair_size = reg_bytes + pad_bytes + val_bytes; +	size_t len = pair_size * num_regs; + +	if (!len) +		return -EINVAL; + +	buf = kzalloc(len, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	/* We have to linearise by hand. */ + +	u8 = buf; + +	for (i = 0; i < num_regs; i++) { +		int reg = regs[i].reg; +		int val = regs[i].def; +		trace_regmap_hw_write_start(map->dev, reg, 1); +		map->format.format_reg(u8, reg, map->reg_shift); +		u8 += reg_bytes + pad_bytes; +		map->format.format_val(u8, val, 0); +		u8 += val_bytes; +	} +	u8 = buf; +	*u8 |= map->write_flag_mask; + +	ret = map->bus->write(map->bus_context, buf, len); + +	kfree(buf); + +	for (i = 0; i < num_regs; i++) { +		int reg = regs[i].reg; +		trace_regmap_hw_write_done(map->dev, reg, 1);  	} +	return ret; +} + +static unsigned int _regmap_register_page(struct regmap *map, +					  unsigned int reg, +					  struct regmap_range_node *range) +{ +	unsigned int win_page = (reg - range->range_min) / range->window_len; + +	return win_page; +} + +static int _regmap_range_multi_paged_reg_write(struct regmap *map, +					       struct reg_default *regs, +					       size_t num_regs) +{ +	int ret; +	int i, n; +	struct reg_default *base; +	unsigned int this_page = 0;  	/* -	 * Some devices does not support bulk write, for -	 * them we have a series of single write operations. +	 * the set of registers are not neccessarily in order, but +	 * since the order of write must be preserved this algorithm +	 * chops the set each time the page changes  	 */ -	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); +	base = regs; +	for (i = 0, n = 0; i < num_regs; i++, n++) { +		unsigned int reg = regs[i].reg; +		struct regmap_range_node *range; + +		range = _regmap_range_lookup(map, reg); +		if (range) { +			unsigned int win_page = _regmap_register_page(map, reg, +								      range); + +			if (i == 0) +				this_page = win_page; +			if (win_page != this_page) { +				this_page = win_page; +				ret = _regmap_raw_multi_reg_write(map, base, n); +				if (ret != 0) +					return ret; +				base += n; +				n = 0; +			} +			ret = _regmap_select_page(map, &base[n].reg, range, 1);  			if (ret != 0)  				return ret;  		} -	} else { -		ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count, -					false);  	} +	if (n > 0) +		return _regmap_raw_multi_reg_write(map, base, n); +	return 0; +} -	if (val_bytes != 1) -		kfree(wval); +static int _regmap_multi_reg_write(struct regmap *map, +				   const struct reg_default *regs, +				   size_t num_regs) +{ +	int i; +	int ret; + +	if (!map->can_multi_write) { +		for (i = 0; i < num_regs; i++) { +			ret = _regmap_write(map, regs[i].reg, regs[i].def); +			if (ret != 0) +				return ret; +		} +		return 0; +	} + +	if (!map->format.parse_inplace) +		return -EINVAL; + +	if (map->writeable_reg) +		for (i = 0; i < num_regs; i++) { +			int reg = regs[i].reg; +			if (!map->writeable_reg(map->dev, reg)) +				return -EINVAL; +			if (reg % map->reg_stride) +				return -EINVAL; +		} + +	if (!map->cache_bypass) { +		for (i = 0; i < num_regs; i++) { +			unsigned int val = regs[i].def; +			unsigned int reg = regs[i].reg; +			ret = regcache_write(map, reg, val); +			if (ret) { +				dev_err(map->dev, +				"Error in caching of register: %x ret: %d\n", +								reg, ret); +				return ret; +			} +		} +		if (map->cache_only) { +			map->cache_dirty = true; +			return 0; +		} +	} + +	WARN_ON(!map->bus); + +	for (i = 0; i < num_regs; i++) { +		unsigned int reg = regs[i].reg; +		struct regmap_range_node *range; +		range = _regmap_range_lookup(map, reg); +		if (range) { +			size_t len = sizeof(struct reg_default)*num_regs; +			struct reg_default *base = kmemdup(regs, len, +							   GFP_KERNEL); +			if (!base) +				return -ENOMEM; +			ret = _regmap_range_multi_paged_reg_write(map, base, +								  num_regs); +			kfree(base); + +			return ret; +		} +	} +	return _regmap_raw_multi_reg_write(map, regs, num_regs); +} + +/* + * regmap_multi_reg_write(): Write multiple registers to the device + * + * where the set of register,value pairs are supplied in any order, + * possibly not all in a single range. + * + * @map: Register map to write to + * @regs: Array of structures containing register,value to be written + * @num_regs: Number of registers to write + * + * The 'normal' block write mode will send ultimately send data on the + * target bus as R,V1,V2,V3,..,Vn where successively higer registers are + * addressed. However, this alternative block multi write mode will send + * the data as R1,V1,R2,V2,..,Rn,Vn on the target bus. The target device + * must of course support the mode. + * + * A value of zero will be returned on success, a negative errno will be + * returned in error cases. + */ +int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, +			   int num_regs) +{ +	int ret; + +	map->lock(map->lock_arg); + +	ret = _regmap_multi_reg_write(map, regs, num_regs); -out:  	map->unlock(map->lock_arg); +  	return ret;  } -EXPORT_SYMBOL_GPL(regmap_bulk_write); +EXPORT_SYMBOL_GPL(regmap_multi_reg_write); + +/* + * regmap_multi_reg_write_bypassed(): Write multiple registers to the + *                                    device but not the cache + * + * where the set of register are supplied in any order + * + * @map: Register map to write to + * @regs: Array of structures containing register,value to be written + * @num_regs: Number of registers to write + * + * This function is intended to be used for writing a large block of data + * atomically to the device in single transfer for those I2C client devices + * that implement this alternative block write mode. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_multi_reg_write_bypassed(struct regmap *map, +				    const struct reg_default *regs, +				    int num_regs) +{ +	int ret; +	bool bypass; + +	map->lock(map->lock_arg); + +	bypass = map->cache_bypass; +	map->cache_bypass = true; + +	ret = _regmap_multi_reg_write(map, regs, num_regs); + +	map->cache_bypass = bypass; + +	map->unlock(map->lock_arg); + +	return ret; +} +EXPORT_SYMBOL_GPL(regmap_multi_reg_write_bypassed);  /**   * regmap_raw_write_async(): Write raw values to one or more registers @@ -1473,7 +1946,11 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,  	map->lock(map->lock_arg); -	ret = _regmap_raw_write(map, reg, val, val_len, true); +	map->async = true; + +	ret = _regmap_raw_write(map, reg, val, val_len); + +	map->async = false;  	map->unlock(map->lock_arg); @@ -1521,6 +1998,14 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,  	return ret;  } +static int _regmap_bus_reg_read(void *context, unsigned int reg, +				unsigned int *val) +{ +	struct regmap *map = context; + +	return map->bus->reg_read(map->bus_context, reg, val); +} +  static int _regmap_bus_read(void *context, unsigned int reg,  			    unsigned int *val)  { @@ -1554,6 +2039,9 @@ static int _regmap_read(struct regmap *map, unsigned int reg,  	if (map->cache_only)  		return -EBUSY; +	if (!regmap_readable(map, reg)) +		return -EIO; +  	ret = map->reg_read(context, reg, val);  	if (ret == 0) {  #ifdef LOG_DEVICE @@ -1573,7 +2061,7 @@ static int _regmap_read(struct regmap *map, unsigned int reg,  /**   * regmap_read(): Read a value from a single register   * - * @map: Register map to write to + * @map: Register map to read from   * @reg: Register to be read from   * @val: Pointer to store read value   * @@ -1600,7 +2088,7 @@ EXPORT_SYMBOL_GPL(regmap_read);  /**   * regmap_raw_read(): Read raw data from the device   * - * @map: Register map to write to + * @map: Register map to read from   * @reg: First register to be read from   * @val: Pointer to store read value   * @val_len: Size of data to read @@ -1677,9 +2165,42 @@ int regmap_field_read(struct regmap_field *field, unsigned int *val)  EXPORT_SYMBOL_GPL(regmap_field_read);  /** + * regmap_fields_read(): Read a value to a single register field with port ID + * + * @field: Register field to read from + * @id: port ID + * @val: Pointer to store read value + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_read(struct regmap_field *field, unsigned int id, +		       unsigned int *val) +{ +	int ret; +	unsigned int reg_val; + +	if (id >= field->id_size) +		return -EINVAL; + +	ret = regmap_read(field->regmap, +			  field->reg + (field->id_offset * id), +			  ®_val); +	if (ret != 0) +		return ret; + +	reg_val &= field->mask; +	reg_val >>= field->shift; +	*val = reg_val; + +	return ret; +} +EXPORT_SYMBOL_GPL(regmap_fields_read); + +/**   * regmap_bulk_read(): Read multiple registers from the device   * - * @map: Register map to write to + * @map: Register map to read from   * @reg: First register to be read from   * @val: Pointer to store read value, in native register size for device   * @val_count: Number of registers to read @@ -1694,14 +2215,10 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,  	size_t val_bytes = map->format.val_bytes;  	bool vol = regmap_volatile_range(map, reg, val_count); -	if (!map->bus) -		return -EINVAL; -	if (!map->format.parse_inplace) -		return -EINVAL;  	if (reg % map->reg_stride)  		return -EINVAL; -	if (vol || map->cache_type == REGCACHE_NONE) { +	if (map->bus && map->format.parse_inplace && (vol || map->cache_type == REGCACHE_NONE)) {  		/*  		 * Some devices does not support bulk read, for  		 * them we have a series of single read operations. @@ -1755,9 +2272,11 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,  	if (tmp != orig) {  		ret = _regmap_write(map, reg, tmp); -		*change = true; +		if (change) +			*change = true;  	} else { -		*change = false; +		if (change) +			*change = false;  	}  	return ret; @@ -1776,11 +2295,10 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,  int regmap_update_bits(struct regmap *map, unsigned int reg,  		       unsigned int mask, unsigned int val)  { -	bool change;  	int ret;  	map->lock(map->lock_arg); -	ret = _regmap_update_bits(map, reg, mask, val, &change); +	ret = _regmap_update_bits(map, reg, mask, val, NULL);  	map->unlock(map->lock_arg);  	return ret; @@ -1788,6 +2306,40 @@ int regmap_update_bits(struct regmap *map, unsigned int reg,  EXPORT_SYMBOL_GPL(regmap_update_bits);  /** + * regmap_update_bits_async: Perform a read/modify/write cycle on the register + *                           map asynchronously + * + * @map: Register map to update + * @reg: Register to update + * @mask: Bitmask to change + * @val: New value for bitmask + * + * With most buses the read must be done synchronously so this is most + * useful for devices with a cache which do not need to interact with + * the hardware to determine the current register value. + * + * Returns zero for success, a negative number on error. + */ +int regmap_update_bits_async(struct regmap *map, unsigned int reg, +			     unsigned int mask, unsigned int val) +{ +	int ret; + +	map->lock(map->lock_arg); + +	map->async = true; + +	ret = _regmap_update_bits(map, reg, mask, val, NULL); + +	map->async = false; + +	map->unlock(map->lock_arg); + +	return ret; +} +EXPORT_SYMBOL_GPL(regmap_update_bits_async); + +/**   * regmap_update_bits_check: Perform a read/modify/write cycle on the   *                           register map and report if updated   * @@ -1812,6 +2364,43 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,  }  EXPORT_SYMBOL_GPL(regmap_update_bits_check); +/** + * regmap_update_bits_check_async: Perform a read/modify/write cycle on the + *                                 register map asynchronously and report if + *                                 updated + * + * @map: Register map to update + * @reg: Register to update + * @mask: Bitmask to change + * @val: New value for bitmask + * @change: Boolean indicating if a write was done + * + * With most buses the read must be done synchronously so this is most + * useful for devices with a cache which do not need to interact with + * the hardware to determine the current register value. + * + * Returns zero for success, a negative number on error. + */ +int regmap_update_bits_check_async(struct regmap *map, unsigned int reg, +				   unsigned int mask, unsigned int val, +				   bool *change) +{ +	int ret; + +	map->lock(map->lock_arg); + +	map->async = true; + +	ret = _regmap_update_bits(map, reg, mask, val, change); + +	map->async = false; + +	map->unlock(map->lock_arg); + +	return ret; +} +EXPORT_SYMBOL_GPL(regmap_update_bits_check_async); +  void regmap_async_complete_cb(struct regmap_async *async, int ret)  {  	struct regmap *map = async->map; @@ -1820,8 +2409,7 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret)  	trace_regmap_async_io_complete(map->dev);  	spin_lock(&map->async_lock); - -	list_del(&async->list); +	list_move(&async->list, &map->async_free);  	wake = list_empty(&map->async_list);  	if (ret != 0) @@ -1829,8 +2417,6 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret)  	spin_unlock(&map->async_lock); -	schedule_work(&async->cleanup); -  	if (wake)  		wake_up(&map->async_waitq);  } @@ -1893,29 +2479,20 @@ EXPORT_SYMBOL_GPL(regmap_async_complete);   * apply them immediately.  Typically this is used to apply   * corrections to be applied to the device defaults on startup, such   * as the updates some vendors provide to undocumented registers. + * + * The caller must ensure that this function cannot be called + * concurrently with either itself or regcache_sync().   */  int regmap_register_patch(struct regmap *map, const struct reg_default *regs,  			  int num_regs)  {  	struct reg_default *p; -	int i, ret; +	int ret;  	bool bypass; -	map->lock(map->lock_arg); - -	bypass = map->cache_bypass; - -	map->cache_bypass = true; - -	/* Write out first; it's useful to apply even if we fail later. */ -	for (i = 0; i < num_regs; i++) { -		ret = _regmap_write(map, regs[i].reg, regs[i].def); -		if (ret != 0) { -			dev_err(map->dev, "Failed to write %x = %x: %d\n", -				regs[i].reg, regs[i].def, ret); -			goto out; -		} -	} +	if (WARN_ONCE(num_regs <= 0, "invalid registers number (%d)\n", +	    num_regs)) +		return 0;  	p = krealloc(map->patch,  		     sizeof(struct reg_default) * (map->patch_regs + num_regs), @@ -1925,14 +2502,28 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,  		map->patch = p;  		map->patch_regs += num_regs;  	} else { -		ret = -ENOMEM; +		return -ENOMEM;  	} +	map->lock(map->lock_arg); + +	bypass = map->cache_bypass; + +	map->cache_bypass = true; +	map->async = true; + +	ret = _regmap_multi_reg_write(map, regs, num_regs); +	if (ret != 0) +		goto out; +  out: +	map->async = false;  	map->cache_bypass = bypass;  	map->unlock(map->lock_arg); +	regmap_async_complete(map); +  	return ret;  }  EXPORT_SYMBOL_GPL(regmap_register_patch); @@ -1952,6 +2543,18 @@ int regmap_get_val_bytes(struct regmap *map)  }  EXPORT_SYMBOL_GPL(regmap_get_val_bytes); +int regmap_parse_val(struct regmap *map, const void *buf, +			unsigned int *val) +{ +	if (!map->format.parse_val) +		return -EINVAL; + +	*val = map->format.parse_val(buf); + +	return 0; +} +EXPORT_SYMBOL_GPL(regmap_parse_val); +  static int __init regmap_initcall(void)  {  	regmap_debugfs_initcall();  | 
