Commit 6f10810c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull regmap updates from Mark Brown:
 "There's one big bit of work this time around, the addition of support
  for a greater range of MBQ access sizes to SoundWire devices together
  with support for deferred read/write.

  The MBQ register maps generally have variable register sizes, the
  variable regiseter size support allows them to be handled much more
  naturally within regmap with less open coding in drivers.

  The deferred read/write support avoids spurious errors when devices
  make use of a bus feature allowing them to indicate they're busy.
  These changes pull in a supporting SoundWire change, and there's an
  ASoC change building off the new code.

  The remainder of the changes are code cleanups"

* tag 'regmap-v6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: sdw-mbq: Add support for SDCA deferred controls
  regmap: sdw-mbq: Add support for further MBQ register sizes
  ASoC: SDCA: Update list of entity_0 controls
  soundwire: SDCA: Add additional SDCA address macros
  regmap: regmap_multi_reg_read(): make register list const
  regmap: cache: rbtree: use krealloc_array() to replace krealloc()
  regmap: cache: mapple: use kmalloc_array() to replace kmalloc()
  regmap: place foo / 8 and foo % 8 closer to each other
  regmap: Use BITS_TO_BYTES()
  regmap: cache: Use BITS_TO_BYTES()
parents 0c934315 d406b354
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -73,8 +73,7 @@ static int regcache_maple_write(struct regmap *map, unsigned int reg,

	rcu_read_unlock();

	entry = kmalloc((last - index + 1) * sizeof(unsigned long),
			map->alloc_flags);
	entry = kmalloc_array(last - index + 1, sizeof(*entry), map->alloc_flags);
	if (!entry)
		return -ENOMEM;

@@ -204,7 +203,7 @@ static int regcache_maple_sync_block(struct regmap *map, unsigned long *entry,
	 * overheads.
	 */
	if (max - min > 1 && regmap_can_raw_write(map)) {
		buf = kmalloc(val_bytes * (max - min), map->alloc_flags);
		buf = kmalloc_array(max - min, val_bytes, map->alloc_flags);
		if (!buf) {
			ret = -ENOMEM;
			goto out;
@@ -320,7 +319,7 @@ static int regcache_maple_insert_block(struct regmap *map, int first,
	unsigned long *entry;
	int i, ret;

	entry = kcalloc(last - first + 1, sizeof(unsigned long), map->alloc_flags);
	entry = kmalloc_array(last - first + 1, sizeof(*entry), map->alloc_flags);
	if (!entry)
		return -ENOMEM;

+4 −6
Original line number Diff line number Diff line
@@ -275,17 +275,15 @@ static int regcache_rbtree_insert_to_block(struct regmap *map,
	pos = (reg - base_reg) / map->reg_stride;
	offset = (rbnode->base_reg - base_reg) / map->reg_stride;

	blk = krealloc(rbnode->block,
		       blklen * map->cache_word_size,
		       map->alloc_flags);
	blk = krealloc_array(rbnode->block, blklen, map->cache_word_size, map->alloc_flags);
	if (!blk)
		return -ENOMEM;

	rbnode->block = blk;

	if (BITS_TO_LONGS(blklen) > BITS_TO_LONGS(rbnode->blklen)) {
		present = krealloc(rbnode->cache_present,
				   BITS_TO_LONGS(blklen) * sizeof(*present),
		present = krealloc_array(rbnode->cache_present,
					 BITS_TO_LONGS(blklen), sizeof(*present),
					 map->alloc_flags);
		if (!present)
			return -ENOMEM;
+1 −1
Original line number Diff line number Diff line
@@ -154,7 +154,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
	map->num_reg_defaults = config->num_reg_defaults;
	map->num_reg_defaults_raw = config->num_reg_defaults_raw;
	map->reg_defaults_raw = config->reg_defaults_raw;
	map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8);
	map->cache_word_size = BITS_TO_BYTES(config->val_bits);
	map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw;

	map->cache = NULL;
+197 −22
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
// Copyright(c) 2020 Intel Corporation.

#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
#include <sound/sdca_function.h>
#include "internal.h"

struct regmap_mbq_context {
	struct device *dev;

	struct regmap_sdw_mbq_cfg cfg;

	int val_size;
	bool (*readable_reg)(struct device *dev, unsigned int reg);
};

static int regmap_sdw_mbq_size(struct regmap_mbq_context *ctx, unsigned int reg)
{
	int size = ctx->val_size;

	if (ctx->cfg.mbq_size) {
		size = ctx->cfg.mbq_size(ctx->dev, reg);
		if (!size || size > ctx->val_size)
			return -EINVAL;
	}

	return size;
}

static bool regmap_sdw_mbq_deferrable(struct regmap_mbq_context *ctx, unsigned int reg)
{
	if (ctx->cfg.deferrable)
		return ctx->cfg.deferrable(ctx->dev, reg);

	return false;
}

static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg,
				    struct regmap_mbq_context *ctx)
{
	struct device *dev = &slave->dev;
	int val, ret = 0;

	dev_dbg(dev, "Deferring transaction for 0x%x\n", reg);

	reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(reg), 0,
			   SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);

	if (ctx->readable_reg(dev, reg)) {
		ret = read_poll_timeout(sdw_read_no_pm, val,
					val < 0 || !(val & SDCA_CTL_ENTITY_0_FUNCTION_BUSY),
					ctx->cfg.timeout_us, ctx->cfg.retry_us,
					false, slave, reg);
		if (val < 0)
			return val;
		if (ret)
			dev_err(dev, "Function busy timed out 0x%x: %d\n", reg, val);
	} else {
		fsleep(ctx->cfg.timeout_us);
	}

	return ret;
}

static int regmap_sdw_mbq_write_impl(struct sdw_slave *slave,
				     unsigned int reg, unsigned int val,
				     int mbq_size, bool deferrable)
{
	int shift = mbq_size * BITS_PER_BYTE;
	int ret;

	while (--mbq_size > 0) {
		shift -= BITS_PER_BYTE;

		ret = sdw_write_no_pm(slave, SDW_SDCA_MBQ_CTL(reg),
				      (val >> shift) & 0xff);
		if (ret < 0)
			return ret;
	}

	ret = sdw_write_no_pm(slave, reg, val & 0xff);
	if (deferrable && ret == -ENODATA)
		return -EAGAIN;

	return ret;
}

static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val)
{
	struct device *dev = context;
	struct regmap_mbq_context *ctx = context;
	struct device *dev = ctx->dev;
	struct sdw_slave *slave = dev_to_sdw_dev(dev);
	bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
	int mbq_size = regmap_sdw_mbq_size(ctx, reg);
	int ret;

	ret = sdw_write_no_pm(slave, SDW_SDCA_MBQ_CTL(reg), (val >> 8) & 0xff);
	if (ret < 0)
	if (mbq_size < 0)
		return mbq_size;

	/*
	 * Technically the spec does allow a device to set itself to busy for
	 * internal reasons, but since it doesn't provide any information on
	 * how to handle timeouts in that case, for now the code will only
	 * process a single wait/timeout on function busy and a single retry
	 * of the transaction.
	 */
	ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, deferrable);
	if (ret == -EAGAIN) {
		ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx);
		if (ret)
			return ret;

	return sdw_write_no_pm(slave, reg, val & 0xff);
		ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, false);
	}

	return ret;
}

static int regmap_sdw_mbq_read_impl(struct sdw_slave *slave,
				    unsigned int reg, unsigned int *val,
				    int mbq_size, bool deferrable)
{
	int shift = BITS_PER_BYTE;
	int read;

	read = sdw_read_no_pm(slave, reg);
	if (read < 0) {
		if (deferrable && read == -ENODATA)
			return -EAGAIN;

		return read;
	}

	*val = read;

	while (--mbq_size > 0) {
		read = sdw_read_no_pm(slave, SDW_SDCA_MBQ_CTL(reg));
		if (read < 0)
			return read;

		*val |= read << shift;
		shift += BITS_PER_BYTE;
	}

	return 0;
}

static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)
{
	struct device *dev = context;
	struct regmap_mbq_context *ctx = context;
	struct device *dev = ctx->dev;
	struct sdw_slave *slave = dev_to_sdw_dev(dev);
	int read0;
	int read1;
	bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
	int mbq_size = regmap_sdw_mbq_size(ctx, reg);
	int ret;

	read0 = sdw_read_no_pm(slave, reg);
	if (read0 < 0)
		return read0;
	if (mbq_size < 0)
		return mbq_size;

	read1 = sdw_read_no_pm(slave, SDW_SDCA_MBQ_CTL(reg));
	if (read1 < 0)
		return read1;
	/*
	 * Technically the spec does allow a device to set itself to busy for
	 * internal reasons, but since it doesn't provide any information on
	 * how to handle timeouts in that case, for now the code will only
	 * process a single wait/timeout on function busy and a single retry
	 * of the transaction.
	 */
	ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, deferrable);
	if (ret == -EAGAIN) {
		ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx);
		if (ret)
			return ret;

	*val = (read1 << 8) | read0;
		ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, false);
	}

	return 0;
	return ret;
}

static const struct regmap_bus regmap_sdw_mbq = {
@@ -51,8 +193,7 @@ static const struct regmap_bus regmap_sdw_mbq = {

static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
{
	/* MBQ-based controls are only 16-bits for now */
	if (config->val_bits != 16)
	if (config->val_bits > (sizeof(unsigned int) * BITS_PER_BYTE))
		return -ENOTSUPP;

	/* Registers are 32 bits wide */
@@ -65,35 +206,69 @@ static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
	return 0;
}

static struct regmap_mbq_context *
regmap_sdw_mbq_gen_context(struct device *dev,
			   const struct regmap_config *config,
			   const struct regmap_sdw_mbq_cfg *mbq_config)
{
	struct regmap_mbq_context *ctx;

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

	ctx->dev = dev;

	if (mbq_config)
		ctx->cfg = *mbq_config;

	ctx->val_size = config->val_bits / BITS_PER_BYTE;
	ctx->readable_reg = config->readable_reg;

	return ctx;
}

struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
				     const struct regmap_config *config,
				     const struct regmap_sdw_mbq_cfg *mbq_config,
				     struct lock_class_key *lock_key,
				     const char *lock_name)
{
	struct regmap_mbq_context *ctx;
	int ret;

	ret = regmap_sdw_mbq_config_check(config);
	if (ret)
		return ERR_PTR(ret);

	return __regmap_init(&sdw->dev, &regmap_sdw_mbq,
			&sdw->dev, config, lock_key, lock_name);
	ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config);
	if (IS_ERR(ctx))
		return ERR_CAST(ctx);

	return __regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx,
			     config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_sdw_mbq);

struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
					  const struct regmap_config *config,
					  const struct regmap_sdw_mbq_cfg *mbq_config,
					  struct lock_class_key *lock_key,
					  const char *lock_name)
{
	struct regmap_mbq_context *ctx;
	int ret;

	ret = regmap_sdw_mbq_config_check(config);
	if (ret)
		return ERR_PTR(ret);

	return __devm_regmap_init(&sdw->dev, &regmap_sdw_mbq,
			&sdw->dev, config, lock_key, lock_name);
	ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config);
	if (IS_ERR(ctx))
		return ERR_CAST(ctx);

	return __devm_regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx,
				  config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq);

+6 −7
Original line number Diff line number Diff line
@@ -769,14 +769,13 @@ struct regmap *__regmap_init(struct device *dev,
		map->alloc_flags = GFP_KERNEL;

	map->reg_base = config->reg_base;
	map->reg_shift = config->pad_bits % 8;

	map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
	map->format.pad_bytes = config->pad_bits / 8;
	map->format.reg_shift = config->reg_shift;
	map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
	map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
			config->val_bits + config->pad_bits, 8);
	map->reg_shift = config->pad_bits % 8;
	map->format.reg_bytes = BITS_TO_BYTES(config->reg_bits);
	map->format.val_bytes = BITS_TO_BYTES(config->val_bits);
	map->format.buf_size = BITS_TO_BYTES(config->reg_bits + config->val_bits + config->pad_bits);
	if (config->reg_stride)
		map->reg_stride = config->reg_stride;
	else
@@ -3116,7 +3115,7 @@ int regmap_fields_read(struct regmap_field *field, unsigned int id,
EXPORT_SYMBOL_GPL(regmap_fields_read);

static int _regmap_bulk_read(struct regmap *map, unsigned int reg,
			     unsigned int *regs, void *val, size_t val_count)
			     const unsigned int *regs, void *val, size_t val_count)
{
	u32 *u32 = val;
	u16 *u16 = val;
@@ -3210,7 +3209,7 @@ EXPORT_SYMBOL_GPL(regmap_bulk_read);
 * A value of zero will be returned on success, a negative errno will
 * be returned in error cases.
 */
int regmap_multi_reg_read(struct regmap *map, unsigned int *regs, void *val,
int regmap_multi_reg_read(struct regmap *map, const unsigned int *regs, void *val,
			  size_t val_count)
{
	if (val_count == 0)
Loading