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

Commit e81b594c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull regmap updates from Mark Brown:
 "This has been a busy release for regmap.

  By far the biggest set of changes here are those from Markus Pargmann
  which implement support for block transfers in smbus devices.  This
  required quite a bit of refactoring but leaves us better able to
  handle odd restrictions that controllers may have and with better
  performance on smbus.

  Other new features include:

   - Fix interactions with lockdep for nested regmaps (eg, when a device
     using regmap is connected to a bus where the bus controller has a
     separate regmap).  Lockdep's default class identification is too
     crude to work without help.

   - Support for must write bitfield operations, useful for operations
     which require writing a bit to trigger them from Kuniori Morimoto.

   - Support for delaying during register patch application from Nariman
     Poushin.

   - Support for overriding cache state via the debugfs implementation
     from Richard Fitzgerald"

* tag 'regmap-v4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: (25 commits)
  regmap: fix a NULL pointer dereference in __regmap_init
  regmap: Support bulk reads for devices without raw formatting
  regmap-i2c: Add smbus i2c block support
  regmap: Add raw_write/read checks for max_raw_write/read sizes
  regmap: regmap max_raw_read/write getter functions
  regmap: Introduce max_raw_read/write for regmap_bulk_read/write
  regmap: Add missing comments about struct regmap_bus
  regmap: No multi_write support if bus->write does not exist
  regmap: Split use_single_rw internally into use_single_read/write
  regmap: Fix regmap_bulk_write for bus writes
  regmap: regmap_raw_read return error on !bus->read
  regulator: core: Print at debug level on debugfs creation failure
  regmap: Fix regmap_can_raw_write check
  regmap: fix typos in regmap.c
  regmap: Fix integertypes for register address and value
  regmap: Move documentation to regmap.h
  regmap: Use different lockdep class for each regmap init call
  thermal: sti: Add parentheses around bridge->ops->regmap_init call
  mfd: vexpress: Add parentheses around bridge->ops->regmap_init call
  regmap: debugfs: Fix misuse of IS_ENABLED
  ...
parents fa815580 072502a6
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -139,11 +139,17 @@ struct regmap {
	struct reg_sequence *patch;
	int patch_regs;

	/* if set, converts bulk rw to single rw */
	bool use_single_rw;
	/* if set, converts bulk read to single read */
	bool use_single_read;
	/* if set, converts bulk read to single read */
	bool use_single_write;
	/* if set, the device supports multi write mode */
	bool can_multi_write;

	/* if set, raw reads/writes are limited to this size */
	size_t max_raw_read;
	size_t max_raw_write;

	struct rb_root range_tree;
	void *selector_work_buf;	/* Scratch buffer used for selector */
};
+1 −1
Original line number Diff line number Diff line
@@ -729,7 +729,7 @@ int regcache_sync_block(struct regmap *map, void *block,
			unsigned int block_base, unsigned int start,
			unsigned int end)
{
	if (regmap_can_raw_write(map) && !map->use_single_rw)
	if (regmap_can_raw_write(map) && !map->use_single_write)
		return regcache_sync_block_raw(map, block, cache_present,
					       block_base, start, end);
	else
+14 −27
Original line number Diff line number Diff line
@@ -78,37 +78,24 @@ static const struct regmap_bus ac97_regmap_bus = {
	.reg_read = regmap_ac97_reg_read,
};

/**
 * regmap_init_ac97(): Initialise AC'97 register map
 *
 * @ac97: 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_ac97(struct snd_ac97 *ac97,
				const struct regmap_config *config)
struct regmap *__regmap_init_ac97(struct snd_ac97 *ac97,
				  const struct regmap_config *config,
				  struct lock_class_key *lock_key,
				  const char *lock_name)
{
	return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
	return __regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config,
			     lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(regmap_init_ac97);
EXPORT_SYMBOL_GPL(__regmap_init_ac97);

/**
 * devm_regmap_init_ac97(): Initialise AC'97 register map
 *
 * @ac97: 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_ac97(struct snd_ac97 *ac97,
				     const struct regmap_config *config)
struct regmap *__devm_regmap_init_ac97(struct snd_ac97 *ac97,
				       const struct regmap_config *config,
				       struct lock_class_key *lock_key,
				       const char *lock_name)
{
	return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
	return __devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config,
				  lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(devm_regmap_init_ac97);
EXPORT_SYMBOL_GPL(__devm_regmap_init_ac97);

MODULE_LICENSE("GPL v2");
+91 −8
Original line number Diff line number Diff line
@@ -469,6 +469,87 @@ static const struct file_operations regmap_access_fops = {
	.llseek = default_llseek,
};

static ssize_t regmap_cache_only_write_file(struct file *file,
					    const char __user *user_buf,
					    size_t count, loff_t *ppos)
{
	struct regmap *map = container_of(file->private_data,
					  struct regmap, cache_only);
	ssize_t result;
	bool was_enabled, require_sync = false;
	int err;

	map->lock(map->lock_arg);

	was_enabled = map->cache_only;

	result = debugfs_write_file_bool(file, user_buf, count, ppos);
	if (result < 0) {
		map->unlock(map->lock_arg);
		return result;
	}

	if (map->cache_only && !was_enabled) {
		dev_warn(map->dev, "debugfs cache_only=Y forced\n");
		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
	} else if (!map->cache_only && was_enabled) {
		dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n");
		require_sync = true;
	}

	map->unlock(map->lock_arg);

	if (require_sync) {
		err = regcache_sync(map);
		if (err)
			dev_err(map->dev, "Failed to sync cache %d\n", err);
	}

	return result;
}

static const struct file_operations regmap_cache_only_fops = {
	.open = simple_open,
	.read = debugfs_read_file_bool,
	.write = regmap_cache_only_write_file,
};

static ssize_t regmap_cache_bypass_write_file(struct file *file,
					      const char __user *user_buf,
					      size_t count, loff_t *ppos)
{
	struct regmap *map = container_of(file->private_data,
					  struct regmap, cache_bypass);
	ssize_t result;
	bool was_enabled;

	map->lock(map->lock_arg);

	was_enabled = map->cache_bypass;

	result = debugfs_write_file_bool(file, user_buf, count, ppos);
	if (result < 0)
		goto out;

	if (map->cache_bypass && !was_enabled) {
		dev_warn(map->dev, "debugfs cache_bypass=Y forced\n");
		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
	} else if (!map->cache_bypass && was_enabled) {
		dev_warn(map->dev, "debugfs cache_bypass=N forced\n");
	}

out:
	map->unlock(map->lock_arg);

	return result;
}

static const struct file_operations regmap_cache_bypass_fops = {
	.open = simple_open,
	.read = debugfs_read_file_bool,
	.write = regmap_cache_bypass_write_file,
};

void regmap_debugfs_init(struct regmap *map, const char *name)
{
	struct rb_node *next;
@@ -518,10 +599,11 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
	if (map->max_register || regmap_readable(map, 0)) {
		umode_t registers_mode;

		if (IS_ENABLED(REGMAP_ALLOW_WRITE_DEBUGFS))
#if defined(REGMAP_ALLOW_WRITE_DEBUGFS)
		registers_mode = 0600;
		else
#else
		registers_mode = 0400;
#endif

		debugfs_create_file("registers", registers_mode, map->debugfs,
				    map, &regmap_map_fops);
@@ -530,12 +612,13 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
	}

	if (map->cache_type) {
		debugfs_create_bool("cache_only", 0400, map->debugfs,
				    &map->cache_only);
		debugfs_create_file("cache_only", 0600, map->debugfs,
				    &map->cache_only, &regmap_cache_only_fops);
		debugfs_create_bool("cache_dirty", 0400, map->debugfs,
				    &map->cache_dirty);
		debugfs_create_bool("cache_bypass", 0400, map->debugfs,
				    &map->cache_bypass);
		debugfs_create_file("cache_bypass", 0600, map->debugfs,
				    &map->cache_bypass,
				    &regmap_cache_bypass_fops);
	}

	next = rb_first(&map->range_tree);
+63 −27
Original line number Diff line number Diff line
@@ -209,11 +209,60 @@ static struct regmap_bus regmap_i2c = {
	.val_format_endian_default = REGMAP_ENDIAN_BIG,
};

static int regmap_i2c_smbus_i2c_write(void *context, const void *data,
				      size_t count)
{
	struct device *dev = context;
	struct i2c_client *i2c = to_i2c_client(dev);

	if (count < 1)
		return -EINVAL;
	if (count >= I2C_SMBUS_BLOCK_MAX)
		return -E2BIG;

	--count;
	return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
					      ((u8 *)data + 1));
}

static int regmap_i2c_smbus_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);
	int ret;

	if (reg_size != 1 || val_size < 1)
		return -EINVAL;
	if (val_size >= I2C_SMBUS_BLOCK_MAX)
		return -E2BIG;

	ret = i2c_smbus_read_i2c_block_data(i2c, ((u8 *)reg)[0], val_size, val);
	if (ret == val_size)
		return 0;
	else if (ret < 0)
		return ret;
	else
		return -EIO;
}

static struct regmap_bus regmap_i2c_smbus_i2c_block = {
	.write = regmap_i2c_smbus_i2c_write,
	.read = regmap_i2c_smbus_i2c_read,
	.max_raw_read = I2C_SMBUS_BLOCK_MAX,
	.max_raw_write = I2C_SMBUS_BLOCK_MAX,
};

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 &regmap_i2c;
	else if (config->reg_bits == 8 &&
		 i2c_check_functionality(i2c->adapter,
					 I2C_FUNC_SMBUS_I2C_BLOCK))
		return &regmap_i2c_smbus_i2c_block;
	else if (config->val_bits == 16 && config->reg_bits == 8 &&
		 i2c_check_functionality(i2c->adapter,
					 I2C_FUNC_SMBUS_WORD_DATA))
@@ -233,47 +282,34 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
	return ERR_PTR(-ENOTSUPP);
}

/**
 * regmap_init_i2c(): Initialise register map
 *
 * @i2c: 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_i2c(struct i2c_client *i2c,
			       const struct regmap_config *config)
struct regmap *__regmap_init_i2c(struct i2c_client *i2c,
				 const struct regmap_config *config,
				 struct lock_class_key *lock_key,
				 const char *lock_name)
{
	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);
	return __regmap_init(&i2c->dev, bus, &i2c->dev, config,
			     lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(regmap_init_i2c);
EXPORT_SYMBOL_GPL(__regmap_init_i2c);

/**
 * devm_regmap_init_i2c(): Initialise managed register map
 *
 * @i2c: 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_i2c(struct i2c_client *i2c,
				    const struct regmap_config *config)
struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c,
				      const struct regmap_config *config,
				      struct lock_class_key *lock_key,
				      const char *lock_name)
{
	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);
	return __devm_regmap_init(&i2c->dev, bus, &i2c->dev, config,
				  lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
EXPORT_SYMBOL_GPL(__devm_regmap_init_i2c);

MODULE_LICENSE("GPL");
Loading