Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7cd58b0a authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull regmap updates from Mark Brown:
 "A few fixes plus a few features, the most generally useful thing being
  the register paging support which can be used by quite a few devices:

   - Support for wake IRQs in regmap-irq
   - Support for register paging
   - Support for explicitly specified endianness, mostly for MMIO."

* tag 'regmap-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: Fix incorrect arguments to kzalloc() call
  regmap: Add hook for printk logging for debugging during early init
  regmap: Fix work_buf switching for page update during virtual range access.
  regmap: Add support for register indirect addressing.
  regmap: Move lock out from internal function _regmap_update_bits().
  regmap: mmio: Staticize regmap_mmio_gen_context()
  regmap: Remove warning on stubbed dev_get_regmap()
  regmap: Implement support for wake IRQs
  regmap: Don't try to map non-existant IRQs
  regmap: Constify regmap_irq_chip
  regmap: mmio: request native endian formatting
  regmap: allow busses to request formatting with specific endianness
parents e2b34e31 38e23194
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -95,6 +95,9 @@ struct regmap {

	/* if set, converts bulk rw to single rw */
	bool use_single_rw;

	struct rb_root range_tree;
	void *selector_work_buf;	/* Scratch buffer used for selector */
};

struct regcache_ops {
@@ -115,6 +118,20 @@ bool regmap_precious(struct regmap *map, unsigned int reg);
int _regmap_write(struct regmap *map, unsigned int reg,
		  unsigned int val);

struct regmap_range_node {
	struct rb_node node;

	unsigned int range_min;
	unsigned int range_max;

	unsigned int selector_reg;
	unsigned int selector_mask;
	int selector_shift;

	unsigned int window_start;
	unsigned int window_len;
};

#ifdef CONFIG_DEBUG_FS
extern void regmap_debugfs_initcall(void);
extern void regmap_debugfs_init(struct regmap *map, const char *name);
+54 −3
Original line number Diff line number Diff line
@@ -24,14 +24,18 @@ struct regmap_irq_chip_data {
	struct mutex lock;

	struct regmap *map;
	struct regmap_irq_chip *chip;
	const struct regmap_irq_chip *chip;

	int irq_base;
	struct irq_domain *domain;

	int irq;
	int wake_count;

	unsigned int *status_buf;
	unsigned int *mask_buf;
	unsigned int *mask_buf_def;
	unsigned int *wake_buf;

	unsigned int irq_reg_stride;
};
@@ -71,6 +75,16 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
				d->chip->mask_base + (i * map->reg_stride));
	}

	/* If we've changed our wakeup count propagate it to the parent */
	if (d->wake_count < 0)
		for (i = d->wake_count; i < 0; i++)
			irq_set_irq_wake(d->irq, 0);
	else if (d->wake_count > 0)
		for (i = 0; i < d->wake_count; i++)
			irq_set_irq_wake(d->irq, 1);

	d->wake_count = 0;

	mutex_unlock(&d->lock);
}

@@ -92,18 +106,41 @@ static void regmap_irq_disable(struct irq_data *data)
	d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
}

static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
{
	struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
	struct regmap *map = d->map;
	const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);

	if (!d->chip->wake_base)
		return -EINVAL;

	if (on) {
		d->wake_buf[irq_data->reg_offset / map->reg_stride]
			&= ~irq_data->mask;
		d->wake_count++;
	} else {
		d->wake_buf[irq_data->reg_offset / map->reg_stride]
			|= irq_data->mask;
		d->wake_count--;
	}

	return 0;
}

static struct irq_chip regmap_irq_chip = {
	.name			= "regmap",
	.irq_bus_lock		= regmap_irq_lock,
	.irq_bus_sync_unlock	= regmap_irq_sync_unlock,
	.irq_disable		= regmap_irq_disable,
	.irq_enable		= regmap_irq_enable,
	.irq_set_wake		= regmap_irq_set_wake,
};

static irqreturn_t regmap_irq_thread(int irq, void *d)
{
	struct regmap_irq_chip_data *data = d;
	struct regmap_irq_chip *chip = data->chip;
	const struct regmap_irq_chip *chip = data->chip;
	struct regmap *map = data->map;
	int ret, i;
	bool handled = false;
@@ -195,7 +232,7 @@ static struct irq_domain_ops regmap_domain_ops = {
 * register values used by the IRQ controller over suspend and resume.
 */
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
			int irq_base, struct regmap_irq_chip *chip,
			int irq_base, const struct regmap_irq_chip *chip,
			struct regmap_irq_chip_data **data)
{
	struct regmap_irq_chip_data *d;
@@ -240,6 +277,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
	if (!d->mask_buf_def)
		goto err_alloc;

	if (chip->wake_base) {
		d->wake_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
				      GFP_KERNEL);
		if (!d->wake_buf)
			goto err_alloc;
	}

	d->irq = irq;
	d->map = map;
	d->chip = chip;
	d->irq_base = irq_base;
@@ -294,6 +339,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
err_domain:
	/* Should really dispose of the domain but... */
err_alloc:
	kfree(d->wake_buf);
	kfree(d->mask_buf_def);
	kfree(d->mask_buf);
	kfree(d->status_buf);
@@ -315,6 +361,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)

	free_irq(irq, d);
	/* We should unmap the domain but... */
	kfree(d->wake_buf);
	kfree(d->mask_buf_def);
	kfree(d->mask_buf);
	kfree(d->status_buf);
@@ -346,6 +393,10 @@ EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base);
 */
int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq)
{
	/* Handle holes in the IRQ list */
	if (!data->chip->irqs[irq].mask)
		return -EINVAL;

	return irq_create_mapping(data->domain, irq);
}
EXPORT_SYMBOL_GPL(regmap_irq_get_virq);
+20 −10
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ static int regmap_mmio_gather_write(void *context,

	BUG_ON(reg_size != 4);

	offset = be32_to_cpup(reg);
	offset = *(u32 *)reg;

	while (val_size) {
		switch (ctx->val_bytes) {
@@ -45,14 +45,14 @@ static int regmap_mmio_gather_write(void *context,
			writeb(*(u8 *)val, ctx->regs + offset);
			break;
		case 2:
			writew(be16_to_cpup(val), ctx->regs + offset);
			writew(*(u16 *)val, ctx->regs + offset);
			break;
		case 4:
			writel(be32_to_cpup(val), ctx->regs + offset);
			writel(*(u32 *)val, ctx->regs + offset);
			break;
#ifdef CONFIG_64BIT
		case 8:
			writeq(be64_to_cpup(val), ctx->regs + offset);
			writeq(*(u64 *)val, ctx->regs + offset);
			break;
#endif
		default:
@@ -83,7 +83,7 @@ static int regmap_mmio_read(void *context,

	BUG_ON(reg_size != 4);

	offset = be32_to_cpup(reg);
	offset = *(u32 *)reg;

	while (val_size) {
		switch (ctx->val_bytes) {
@@ -91,14 +91,14 @@ static int regmap_mmio_read(void *context,
			*(u8 *)val = readb(ctx->regs + offset);
			break;
		case 2:
			*(u16 *)val = cpu_to_be16(readw(ctx->regs + offset));
			*(u16 *)val = readw(ctx->regs + offset);
			break;
		case 4:
			*(u32 *)val = cpu_to_be32(readl(ctx->regs + offset));
			*(u32 *)val = readl(ctx->regs + offset);
			break;
#ifdef CONFIG_64BIT
		case 8:
			*(u64 *)val = cpu_to_be32(readq(ctx->regs + offset));
			*(u64 *)val = readq(ctx->regs + offset);
			break;
#endif
		default:
@@ -124,9 +124,11 @@ static struct regmap_bus regmap_mmio = {
	.gather_write = regmap_mmio_gather_write,
	.read = regmap_mmio_read,
	.free_context = regmap_mmio_free_context,
	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
	.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};

struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
					const struct regmap_config *config)
{
	struct regmap_mmio_context *ctx;
@@ -162,7 +164,15 @@ struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
	if (config->reg_stride < min_stride)
		return ERR_PTR(-EINVAL);

	ctx = kzalloc(GFP_KERNEL, sizeof(*ctx));
	switch (config->reg_format_endian) {
	case REGMAP_ENDIAN_DEFAULT:
	case REGMAP_ENDIAN_NATIVE:
		break;
	default:
		return ERR_PTR(-EINVAL);
	}

	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
	if (!ctx)
		return ERR_PTR(-ENOMEM);

+320 −24
Original line number Diff line number Diff line
@@ -15,12 +15,25 @@
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/rbtree.h>

#define CREATE_TRACE_POINTS
#include <trace/events/regmap.h>

#include "internal.h"

/*
 * Sometimes for failures during very early init the trace
 * infrastructure isn't available early enough to be used.  For this
 * sort of problem defining LOG_DEVICE will add printks for basic
 * register I/O on a specific device.
 */
#undef LOG_DEVICE

static int _regmap_update_bits(struct regmap *map, unsigned int reg,
			       unsigned int mask, unsigned int val,
			       bool *change);

bool regmap_writeable(struct regmap *map, unsigned int reg)
{
	if (map->max_register && reg > map->max_register)
@@ -119,13 +132,19 @@ static void regmap_format_8(void *buf, unsigned int val, unsigned int shift)
	b[0] = val << shift;
}

static void regmap_format_16(void *buf, unsigned int val, unsigned int shift)
static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift)
{
	__be16 *b = buf;

	b[0] = cpu_to_be16(val << shift);
}

static void regmap_format_16_native(void *buf, unsigned int val,
				    unsigned int shift)
{
	*(u16 *)buf = val << shift;
}

static void regmap_format_24(void *buf, unsigned int val, unsigned int shift)
{
	u8 *b = buf;
@@ -137,13 +156,19 @@ static void regmap_format_24(void *buf, unsigned int val, unsigned int shift)
	b[2] = val;
}

static void regmap_format_32(void *buf, unsigned int val, unsigned int shift)
static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift)
{
	__be32 *b = buf;

	b[0] = cpu_to_be32(val << shift);
}

static void regmap_format_32_native(void *buf, unsigned int val,
				    unsigned int shift)
{
	*(u32 *)buf = val << shift;
}

static unsigned int regmap_parse_8(void *buf)
{
	u8 *b = buf;
@@ -151,7 +176,7 @@ static unsigned int regmap_parse_8(void *buf)
	return b[0];
}

static unsigned int regmap_parse_16(void *buf)
static unsigned int regmap_parse_16_be(void *buf)
{
	__be16 *b = buf;

@@ -160,6 +185,11 @@ static unsigned int regmap_parse_16(void *buf)
	return b[0];
}

static unsigned int regmap_parse_16_native(void *buf)
{
	return *(u16 *)buf;
}

static unsigned int regmap_parse_24(void *buf)
{
	u8 *b = buf;
@@ -170,7 +200,7 @@ static unsigned int regmap_parse_24(void *buf)
	return ret;
}

static unsigned int regmap_parse_32(void *buf)
static unsigned int regmap_parse_32_be(void *buf)
{
	__be32 *b = buf;

@@ -179,6 +209,11 @@ static unsigned int regmap_parse_32(void *buf)
	return b[0];
}

static unsigned int regmap_parse_32_native(void *buf)
{
	return *(u32 *)buf;
}

static void regmap_lock_mutex(struct regmap *map)
{
	mutex_lock(&map->mutex);
@@ -208,6 +243,67 @@ static void dev_get_regmap_release(struct device *dev, void *res)
	 */
}

static bool _regmap_range_add(struct regmap *map,
			      struct regmap_range_node *data)
{
	struct rb_root *root = &map->range_tree;
	struct rb_node **new = &(root->rb_node), *parent = NULL;

	while (*new) {
		struct regmap_range_node *this =
			container_of(*new, struct regmap_range_node, node);

		parent = *new;
		if (data->range_max < this->range_min)
			new = &((*new)->rb_left);
		else if (data->range_min > this->range_max)
			new = &((*new)->rb_right);
		else
			return false;
	}

	rb_link_node(&data->node, parent, new);
	rb_insert_color(&data->node, root);

	return true;
}

static struct regmap_range_node *_regmap_range_lookup(struct regmap *map,
						      unsigned int reg)
{
	struct rb_node *node = map->range_tree.rb_node;

	while (node) {
		struct regmap_range_node *this =
			container_of(node, struct regmap_range_node, node);

		if (reg < this->range_min)
			node = node->rb_left;
		else if (reg > this->range_max)
			node = node->rb_right;
		else
			return this;
	}

	return NULL;
}

static void regmap_range_exit(struct regmap *map)
{
	struct rb_node *next;
	struct regmap_range_node *range_node;

	next = rb_first(&map->range_tree);
	while (next) {
		range_node = rb_entry(next, struct regmap_range_node, node);
		next = rb_next(&range_node->node);
		rb_erase(&range_node->node, &map->range_tree);
		kfree(range_node);
	}

	kfree(map->selector_work_buf);
}

/**
 * regmap_init(): Initialise register map
 *
@@ -227,6 +323,8 @@ struct regmap *regmap_init(struct device *dev,
{
	struct regmap *map, **m;
	int ret = -EINVAL;
	enum regmap_endian reg_endian, val_endian;
	int i, j;

	if (!bus || !config)
		goto err;
@@ -275,6 +373,18 @@ struct regmap *regmap_init(struct device *dev,
		map->read_flag_mask = bus->read_flag_mask;
	}

	reg_endian = config->reg_format_endian;
	if (reg_endian == REGMAP_ENDIAN_DEFAULT)
		reg_endian = bus->reg_format_endian_default;
	if (reg_endian == REGMAP_ENDIAN_DEFAULT)
		reg_endian = REGMAP_ENDIAN_BIG;

	val_endian = config->val_format_endian;
	if (val_endian == REGMAP_ENDIAN_DEFAULT)
		val_endian = bus->val_format_endian_default;
	if (val_endian == REGMAP_ENDIAN_DEFAULT)
		val_endian = REGMAP_ENDIAN_BIG;

	switch (config->reg_bits + map->reg_shift) {
	case 2:
		switch (config->val_bits) {
@@ -321,11 +431,29 @@ struct regmap *regmap_init(struct device *dev,
		break;

	case 16:
		map->format.format_reg = regmap_format_16;
		switch (reg_endian) {
		case REGMAP_ENDIAN_BIG:
			map->format.format_reg = regmap_format_16_be;
			break;
		case REGMAP_ENDIAN_NATIVE:
			map->format.format_reg = regmap_format_16_native;
			break;
		default:
			goto err_map;
		}
		break;

	case 32:
		map->format.format_reg = regmap_format_32;
		switch (reg_endian) {
		case REGMAP_ENDIAN_BIG:
			map->format.format_reg = regmap_format_32_be;
			break;
		case REGMAP_ENDIAN_NATIVE:
			map->format.format_reg = regmap_format_32_native;
			break;
		default:
			goto err_map;
		}
		break;

	default:
@@ -338,21 +466,47 @@ struct regmap *regmap_init(struct device *dev,
		map->format.parse_val = regmap_parse_8;
		break;
	case 16:
		map->format.format_val = regmap_format_16;
		map->format.parse_val = regmap_parse_16;
		switch (val_endian) {
		case REGMAP_ENDIAN_BIG:
			map->format.format_val = regmap_format_16_be;
			map->format.parse_val = regmap_parse_16_be;
			break;
		case REGMAP_ENDIAN_NATIVE:
			map->format.format_val = regmap_format_16_native;
			map->format.parse_val = regmap_parse_16_native;
			break;
		default:
			goto err_map;
		}
		break;
	case 24:
		if (val_endian != REGMAP_ENDIAN_BIG)
			goto err_map;
		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;
		switch (val_endian) {
		case REGMAP_ENDIAN_BIG:
			map->format.format_val = regmap_format_32_be;
			map->format.parse_val = regmap_parse_32_be;
			break;
		case REGMAP_ENDIAN_NATIVE:
			map->format.format_val = regmap_format_32_native;
			map->format.parse_val = regmap_parse_32_native;
			break;
		default:
			goto err_map;
		}
		break;
	}

	if (map->format.format_write)
	if (map->format.format_write) {
		if ((reg_endian != REGMAP_ENDIAN_BIG) ||
		    (val_endian != REGMAP_ENDIAN_BIG))
			goto err_map;
		map->use_single_rw = true;
	}

	if (!map->format.format_write &&
	    !(map->format.format_reg && map->format.format_val))
@@ -364,27 +518,88 @@ struct regmap *regmap_init(struct device *dev,
		goto err_map;
	}

	regmap_debugfs_init(map, config->name);
	map->range_tree = RB_ROOT;
	for (i = 0; i < config->n_ranges; i++) {
		const struct regmap_range_cfg *range_cfg = &config->ranges[i];
		struct regmap_range_node *new;

		/* Sanity check */
		if (range_cfg->range_max < range_cfg->range_min ||
		    range_cfg->range_max > map->max_register ||
		    range_cfg->selector_reg > map->max_register ||
		    range_cfg->window_len == 0)
			goto err_range;

		/* Make sure, that this register range has no selector
		   or data window within its boundary */
		for (j = 0; j < config->n_ranges; j++) {
			unsigned sel_reg = config->ranges[j].selector_reg;
			unsigned win_min = config->ranges[j].window_start;
			unsigned win_max = win_min +
					   config->ranges[j].window_len - 1;

			if (range_cfg->range_min <= sel_reg &&
			    sel_reg <= range_cfg->range_max) {
				goto err_range;
			}

			if (!(win_max < range_cfg->range_min ||
			      win_min > range_cfg->range_max)) {
				goto err_range;
			}
		}

		new = kzalloc(sizeof(*new), GFP_KERNEL);
		if (new == NULL) {
			ret = -ENOMEM;
			goto err_range;
		}

		new->range_min = range_cfg->range_min;
		new->range_max = range_cfg->range_max;
		new->selector_reg = range_cfg->selector_reg;
		new->selector_mask = range_cfg->selector_mask;
		new->selector_shift = range_cfg->selector_shift;
		new->window_start = range_cfg->window_start;
		new->window_len = range_cfg->window_len;

		if (_regmap_range_add(map, new) == false) {
			kfree(new);
			goto err_range;
		}

		if (map->selector_work_buf == NULL) {
			map->selector_work_buf =
				kzalloc(map->format.buf_size, GFP_KERNEL);
			if (map->selector_work_buf == NULL) {
				ret = -ENOMEM;
				goto err_range;
			}
		}
	}

	ret = regcache_init(map, config);
	if (ret < 0)
		goto err_debugfs;
		goto err_range;

	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) {
		ret = -ENOMEM;
		goto err_cache;
		goto err_debugfs;
	}
	*m = map;
	devres_add(dev, m);

	return map;

err_cache:
	regcache_exit(map);
err_debugfs:
	regmap_debugfs_exit(map);
	regcache_exit(map);
err_range:
	regmap_range_exit(map);
	kfree(map->work_buf);
err_map:
	kfree(map);
@@ -481,6 +696,7 @@ void regmap_exit(struct regmap *map)
{
	regcache_exit(map);
	regmap_debugfs_exit(map);
	regmap_range_exit(map);
	if (map->bus->free_context)
		map->bus->free_context(map->bus_context);
	kfree(map->work_buf);
@@ -526,6 +742,57 @@ struct regmap *dev_get_regmap(struct device *dev, const char *name)
}
EXPORT_SYMBOL_GPL(dev_get_regmap);

static int _regmap_select_page(struct regmap *map, unsigned int *reg,
			       unsigned int val_num)
{
	struct regmap_range_node *range;
	void *orig_work_buf;
	unsigned int win_offset;
	unsigned int win_page;
	bool page_chg;
	int ret;

	range = _regmap_range_lookup(map, *reg);
	if (range) {
		win_offset = (*reg - range->range_min) % range->window_len;
		win_page = (*reg - range->range_min) / range->window_len;

		if (val_num > 1) {
			/* Bulk write shouldn't cross range boundary */
			if (*reg + val_num - 1 > range->range_max)
				return -EINVAL;

			/* ... or single page boundary */
			if (val_num > range->window_len - win_offset)
				return -EINVAL;
		}

		/* It is possible to have selector register inside data window.
		   In that case, selector register is located on every page and
		   it needs no page switching, when accessed alone. */
		if (val_num > 1 ||
		    range->window_start + win_offset != range->selector_reg) {
			/* Use separate work_buf during page switching */
			orig_work_buf = map->work_buf;
			map->work_buf = map->selector_work_buf;

			ret = _regmap_update_bits(map, range->selector_reg,
					range->selector_mask,
					win_page << range->selector_shift,
					&page_chg);

			map->work_buf = orig_work_buf;

			if (ret < 0)
				return ret;
		}

		*reg = range->window_start + win_offset;
	}

	return 0;
}

static int _regmap_raw_write(struct regmap *map, unsigned int reg,
			     const void *val, size_t val_len)
{
@@ -563,6 +830,10 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
		}
	}

	ret = _regmap_select_page(map, &reg, val_len / map->format.val_bytes);
	if (ret < 0)
		return ret;

	map->format.format_reg(map->work_buf, reg, map->reg_shift);

	u8[0] |= map->write_flag_mask;
@@ -623,9 +894,18 @@ int _regmap_write(struct regmap *map, unsigned int reg,
		}
	}

#ifdef LOG_DEVICE
	if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
		dev_info(map->dev, "%x <= %x\n", reg, val);
#endif

	trace_regmap_reg_write(map->dev, reg, val);

	if (map->format.format_write) {
		ret = _regmap_select_page(map, &reg, 1);
		if (ret < 0)
			return ret;

		map->format.format_write(map, reg, val);

		trace_regmap_hw_write_start(map->dev, reg, 1);
@@ -783,6 +1063,10 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
	u8 *u8 = map->work_buf;
	int ret;

	ret = _regmap_select_page(map, &reg, val_len / map->format.val_bytes);
	if (ret < 0)
		return ret;

	map->format.format_reg(map->work_buf, reg, map->reg_shift);

	/*
@@ -826,6 +1110,12 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
	ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
	if (ret == 0) {
		*val = map->format.parse_val(map->work_buf);

#ifdef LOG_DEVICE
		if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
			dev_info(map->dev, "%x => %x\n", reg, *val);
#endif

		trace_regmap_reg_read(map->dev, reg, *val);
	}

@@ -982,11 +1272,9 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
	int ret;
	unsigned int tmp, orig;

	map->lock(map);

	ret = _regmap_read(map, reg, &orig);
	if (ret != 0)
		goto out;
		return ret;

	tmp = orig & ~mask;
	tmp |= val & mask;
@@ -998,9 +1286,6 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
		*change = false;
	}

out:
	map->unlock(map);

	return ret;
}

@@ -1018,7 +1303,13 @@ int regmap_update_bits(struct regmap *map, unsigned int reg,
		       unsigned int mask, unsigned int val)
{
	bool change;
	return _regmap_update_bits(map, reg, mask, val, &change);
	int ret;

	map->lock(map);
	ret = _regmap_update_bits(map, reg, mask, val, &change);
	map->unlock(map);

	return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits);

@@ -1038,7 +1329,12 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
			     unsigned int mask, unsigned int val,
			     bool *change)
{
	return _regmap_update_bits(map, reg, mask, val, change);
	int ret;

	map->lock(map);
	ret = _regmap_update_bits(map, reg, mask, val, change);
	map->unlock(map);
	return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits_check);

+67 −2
Original line number Diff line number Diff line
@@ -14,12 +14,14 @@
 */

#include <linux/list.h>
#include <linux/rbtree.h>

struct module;
struct device;
struct i2c_client;
struct spi_device;
struct regmap;
struct regmap_range_cfg;

/* An enum of all the supported cache types */
enum regcache_type {
@@ -43,6 +45,14 @@ struct reg_default {

#ifdef CONFIG_REGMAP

enum regmap_endian {
	/* Unspecified -> 0 -> Backwards compatible default */
	REGMAP_ENDIAN_DEFAULT = 0,
	REGMAP_ENDIAN_BIG,
	REGMAP_ENDIAN_LITTLE,
	REGMAP_ENDIAN_NATIVE,
};

/**
 * Configuration for the register map of a device.
 *
@@ -84,6 +94,15 @@ struct reg_default {
 * @reg_defaults_raw: Power on reset values for registers (for use with
 *                    register cache support).
 * @num_reg_defaults_raw: Number of elements in reg_defaults_raw.
 * @reg_format_endian: Endianness for formatted register addresses. If this is
 *                     DEFAULT, the @reg_format_endian_default value from the
 *                     regmap bus is used.
 * @val_format_endian: Endianness for formatted register values. If this is
 *                     DEFAULT, the @reg_format_endian_default value from the
 *                     regmap bus is used.
 *
 * @ranges: Array of configuration entries for virtual address ranges.
 * @num_ranges: Number of range configuration entries.
 */
struct regmap_config {
	const char *name;
@@ -109,6 +128,43 @@ struct regmap_config {
	u8 write_flag_mask;

	bool use_single_rw;

	enum regmap_endian reg_format_endian;
	enum regmap_endian val_format_endian;

	const struct regmap_range_cfg *ranges;
	unsigned int n_ranges;
};

/**
 * Configuration for indirectly accessed or paged registers.
 * Registers, mapped to this virtual range, are accessed in two steps:
 *     1. page selector register update;
 *     2. access through data window registers.
 *
 * @range_min: Address of the lowest register address in virtual range.
 * @range_max: Address of the highest register in virtual range.
 *
 * @page_sel_reg: Register with selector field.
 * @page_sel_mask: Bit shift for selector value.
 * @page_sel_shift: Bit mask for selector value.
 *
 * @window_start: Address of first (lowest) register in data window.
 * @window_len: Number of registers in data window.
 */
struct regmap_range_cfg {
	/* Registers of virtual address range */
	unsigned int range_min;
	unsigned int range_max;

	/* Page selector for indirect addressing */
	unsigned int selector_reg;
	unsigned int selector_mask;
	int selector_shift;

	/* Data window (per each page) */
	unsigned int window_start;
	unsigned int window_len;
};

typedef int (*regmap_hw_write)(void *context, const void *data,
@@ -133,6 +189,12 @@ typedef void (*regmap_hw_free_context)(void *context);
 *         data.
 * @read_flag_mask: Mask to be set in the top byte of the register when doing
 *                  a read.
 * @reg_format_endian_default: Default endianness for formatted register
 *     addresses. Used when the regmap_config specifies DEFAULT. If this is
 *     DEFAULT, BIG is assumed.
 * @val_format_endian_default: Default endianness for formatted register
 *     values. Used when the regmap_config specifies DEFAULT. If this is
 *     DEFAULT, BIG is assumed.
 */
struct regmap_bus {
	bool fast_io;
@@ -141,6 +203,8 @@ struct regmap_bus {
	regmap_hw_read read;
	regmap_hw_free_context free_context;
	u8 read_flag_mask;
	enum regmap_endian reg_format_endian_default;
	enum regmap_endian val_format_endian_default;
};

struct regmap *regmap_init(struct device *dev,
@@ -219,6 +283,7 @@ struct regmap_irq {
 * @status_base: Base status register address.
 * @mask_base:   Base mask register address.
 * @ack_base:    Base ack address.  If zero then the chip is clear on read.
 * @wake_base:   Base address for wake enables.  If zero unsupported.
 * @irq_reg_stride:  Stride to use for chips where registers are not contiguous.
 *
 * @num_regs:    Number of registers in each control bank.
@@ -232,6 +297,7 @@ struct regmap_irq_chip {
	unsigned int status_base;
	unsigned int mask_base;
	unsigned int ack_base;
	unsigned int wake_base;
	unsigned int irq_reg_stride;

	int num_regs;
@@ -243,7 +309,7 @@ struct regmap_irq_chip {
struct regmap_irq_chip_data;

int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
			int irq_base, struct regmap_irq_chip *chip,
			int irq_base, const struct regmap_irq_chip *chip,
			struct regmap_irq_chip_data **data);
void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data);
int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data);
@@ -361,7 +427,6 @@ static inline int regmap_register_patch(struct regmap *map,
static inline struct regmap *dev_get_regmap(struct device *dev,
					    const char *name)
{
	WARN_ONCE(1, "regmap API is disabled");
	return NULL;
}