summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-06-28 13:26:19 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2023-06-28 13:26:19 -0700
commit4171a9aa235988fc5cb19d84d493496cb73e6988 (patch)
tree9daf77086005adcb9c08465c057285db1960060b /drivers/base
parent1b2c92a1cb2469d8c0079dbf496ab86e22e1cb7c (diff)
parentd0c99ffe212679b338d12fe283964e6e43ce1501 (diff)
downloadlinux-stable-4171a9aa235988fc5cb19d84d493496cb73e6988.tar.gz
linux-stable-4171a9aa235988fc5cb19d84d493496cb73e6988.tar.bz2
linux-stable-4171a9aa235988fc5cb19d84d493496cb73e6988.zip
Merge tag 'regmap-v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap
Pull regmap updates from Mark Brown: "Another busy release for regmap with the second half of the maple tree register cache implementation, there's some smaller optimisations that could be done but this should now be able to replace the rbtree cache for most devices. We also had a followup from Aidan MacDonald's refactoring of some of the regmap-irq interfaces, the conversion is complete so the old interfaces are removed. This means that even with the new features for the maple tree cache we'd have a nice negative diffstat were it not for the addition of a bunch more KUnit coverage. There's one GPIO patch in here, it was a dependency for a cleanup of an API in the regmap-irq code for which the gpio-104-dio-48e driver was the only user. Highlights: - The maple tree cache can now load in default values more efficiently, and is capabale of syncing multiple registers in a single write during cache sync - More KUnit coverage, including some coverage for raw I/O and a dummy RAM backed cache to support it - Removal of several old interfaces in regmap-irq now all users have been modernised" * tag 'regmap-v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: (23 commits) regmap: Allow reads from write only registers with the flat cache regmap: Drop early readability check regmap: Check for register readability before checking cache during read regmap: Add test to make sure we don't sync to read only registers regmap: Add a test case for write only registers regmap: Add test that writes to write only registers are prevented regmap: Add debugfs file for forcing field writes regmap: Don't check for changes in regcache_set_val() regmap: maple: Implement block sync for the maple tree cache regmap: Provide basic KUnit coverage for the raw register I/O regmap: Provide a ram backed regmap with raw support regmap: Add missing cache_only checks regmap: regmap-irq: Move handle_post_irq to before pm_runtime_put regmap: Load register defaults in blocks rather than register by register regmap: mmio: Allow passing an empty config->reg_stride regmap-irq: Drop backward compatibility for inverted mask/unmask regmap-irq: Minor adjustments to .handle_mask_sync() regmap-irq: Remove support for not_fixed_stride regmap-irq: Remove type registers regmap-irq: Remove virtual registers ...
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/regmap/Makefile2
-rw-r--r--drivers/base/regmap/internal.h15
-rw-r--r--drivers/base/regmap/regcache-maple.c140
-rw-r--r--drivers/base/regmap/regcache.c12
-rw-r--r--drivers/base/regmap/regmap-debugfs.c11
-rw-r--r--drivers/base/regmap/regmap-irq.c273
-rw-r--r--drivers/base/regmap/regmap-kunit.c451
-rw-r--r--drivers/base/regmap/regmap-mmio.c2
-rw-r--r--drivers/base/regmap/regmap-raw-ram.c133
-rw-r--r--drivers/base/regmap/regmap.c28
10 files changed, 808 insertions, 259 deletions
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index f6c6cb017200..5fdd0845b45e 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -8,7 +8,7 @@ obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
obj-$(CONFIG_REGMAP_KUNIT) += regmap-kunit.o
obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
-obj-$(CONFIG_REGMAP_RAM) += regmap-ram.o
+obj-$(CONFIG_REGMAP_RAM) += regmap-ram.o regmap-raw-ram.o
obj-$(CONFIG_REGMAP_SLIMBUS) += regmap-slimbus.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 9bd0dfd1e259..9a9ea514c2d8 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -125,6 +125,9 @@ struct regmap {
int reg_stride;
int reg_stride_order;
+ /* If set, will always write field to HW. */
+ bool force_write_field;
+
/* regcache specific members */
const struct regcache_ops *cache_ops;
enum regcache_type cache_type;
@@ -257,6 +260,8 @@ int regcache_sync_block(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base, unsigned int start,
unsigned int end);
+bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg,
+ unsigned int val);
static inline const void *regcache_get_val_addr(struct regmap *map,
const void *base,
@@ -267,7 +272,7 @@ static inline const void *regcache_get_val_addr(struct regmap *map,
unsigned int regcache_get_val(struct regmap *map, const void *base,
unsigned int idx);
-bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
+void regcache_set_val(struct regmap *map, void *base, unsigned int idx,
unsigned int val);
int regcache_lookup_reg(struct regmap *map, unsigned int reg);
int regcache_sync_val(struct regmap *map, unsigned int reg, unsigned int val);
@@ -312,6 +317,7 @@ struct regmap_ram_data {
unsigned int *vals; /* Allocatd by caller */
bool *read;
bool *written;
+ enum regmap_endian reg_endian;
};
/*
@@ -326,5 +332,12 @@ struct regmap *__regmap_init_ram(const struct regmap_config *config,
#define regmap_init_ram(config, data) \
__regmap_lockdep_wrapper(__regmap_init_ram, #config, config, data)
+struct regmap *__regmap_init_raw_ram(const struct regmap_config *config,
+ struct regmap_ram_data *data,
+ struct lock_class_key *lock_key,
+ const char *lock_name);
+
+#define regmap_init_raw_ram(config, data) \
+ __regmap_lockdep_wrapper(__regmap_init_raw_ram, #config, config, data)
#endif
diff --git a/drivers/base/regmap/regcache-maple.c b/drivers/base/regmap/regcache-maple.c
index c2e3a0f6c218..283c2e02a298 100644
--- a/drivers/base/regmap/regcache-maple.c
+++ b/drivers/base/regmap/regcache-maple.c
@@ -186,6 +186,55 @@ out_unlocked:
return ret;
}
+static int regcache_maple_sync_block(struct regmap *map, unsigned long *entry,
+ struct ma_state *mas,
+ unsigned int min, unsigned int max)
+{
+ void *buf;
+ unsigned long r;
+ size_t val_bytes = map->format.val_bytes;
+ int ret = 0;
+
+ mas_pause(mas);
+ rcu_read_unlock();
+
+ /*
+ * Use a raw write if writing more than one register to a
+ * device that supports raw writes to reduce transaction
+ * overheads.
+ */
+ if (max - min > 1 && regmap_can_raw_write(map)) {
+ buf = kmalloc(val_bytes * (max - min), map->alloc_flags);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Render the data for a raw write */
+ for (r = min; r < max; r++) {
+ regcache_set_val(map, buf, r - min,
+ entry[r - mas->index]);
+ }
+
+ ret = _regmap_raw_write(map, min, buf, (max - min) * val_bytes,
+ false);
+
+ kfree(buf);
+ } else {
+ for (r = min; r < max; r++) {
+ ret = _regmap_write(map, r,
+ entry[r - mas->index]);
+ if (ret != 0)
+ goto out;
+ }
+ }
+
+out:
+ rcu_read_lock();
+
+ return ret;
+}
+
static int regcache_maple_sync(struct regmap *map, unsigned int min,
unsigned int max)
{
@@ -194,8 +243,9 @@ static int regcache_maple_sync(struct regmap *map, unsigned int min,
MA_STATE(mas, mt, min, max);
unsigned long lmin = min;
unsigned long lmax = max;
- unsigned int r;
+ unsigned int r, v, sync_start;
int ret;
+ bool sync_needed = false;
map->cache_bypass = true;
@@ -203,18 +253,38 @@ static int regcache_maple_sync(struct regmap *map, unsigned int min,
mas_for_each(&mas, entry, max) {
for (r = max(mas.index, lmin); r <= min(mas.last, lmax); r++) {
- mas_pause(&mas);
- rcu_read_unlock();
- ret = regcache_sync_val(map, r, entry[r - mas.index]);
+ v = entry[r - mas.index];
+
+ if (regcache_reg_needs_sync(map, r, v)) {
+ if (!sync_needed) {
+ sync_start = r;
+ sync_needed = true;
+ }
+ continue;
+ }
+
+ if (!sync_needed)
+ continue;
+
+ ret = regcache_maple_sync_block(map, entry, &mas,
+ sync_start, r);
+ if (ret != 0)
+ goto out;
+ sync_needed = false;
+ }
+
+ if (sync_needed) {
+ ret = regcache_maple_sync_block(map, entry, &mas,
+ sync_start, r);
if (ret != 0)
goto out;
- rcu_read_lock();
+ sync_needed = false;
}
}
+out:
rcu_read_unlock();
-out:
map->cache_bypass = false;
return ret;
@@ -242,11 +312,41 @@ static int regcache_maple_exit(struct regmap *map)
return 0;
}
+static int regcache_maple_insert_block(struct regmap *map, int first,
+ int last)
+{
+ struct maple_tree *mt = map->cache;
+ MA_STATE(mas, mt, first, last);
+ unsigned long *entry;
+ int i, ret;
+
+ entry = kcalloc(last - first + 1, sizeof(unsigned long), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ for (i = 0; i < last - first + 1; i++)
+ entry[i] = map->reg_defaults[first + i].def;
+
+ mas_lock(&mas);
+
+ mas_set_range(&mas, map->reg_defaults[first].reg,
+ map->reg_defaults[last].reg);
+ ret = mas_store_gfp(&mas, entry, GFP_KERNEL);
+
+ mas_unlock(&mas);
+
+ if (ret)
+ kfree(entry);
+
+ return ret;
+}
+
static int regcache_maple_init(struct regmap *map)
{
struct maple_tree *mt;
int i;
int ret;
+ int range_start;
mt = kmalloc(sizeof(*mt), GFP_KERNEL);
if (!mt)
@@ -255,14 +355,30 @@ static int regcache_maple_init(struct regmap *map)
mt_init(mt);
- for (i = 0; i < map->num_reg_defaults; i++) {
- ret = regcache_maple_write(map,
- map->reg_defaults[i].reg,
- map->reg_defaults[i].def);
- if (ret)
- goto err;
+ if (!map->num_reg_defaults)
+ return 0;
+
+ range_start = 0;
+
+ /* Scan for ranges of contiguous registers */
+ for (i = 1; i < map->num_reg_defaults; i++) {
+ if (map->reg_defaults[i].reg !=
+ map->reg_defaults[i - 1].reg + 1) {
+ ret = regcache_maple_insert_block(map, range_start,
+ i - 1);
+ if (ret != 0)
+ goto err;
+
+ range_start = i;
+ }
}
+ /* Add the last block */
+ ret = regcache_maple_insert_block(map, range_start,
+ map->num_reg_defaults - 1);
+ if (ret != 0)
+ goto err;
+
return 0;
err:
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 97c681fcf9f6..28bc3ae9458a 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -279,8 +279,8 @@ int regcache_write(struct regmap *map,
return 0;
}
-static bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg,
- unsigned int val)
+bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg,
+ unsigned int val)
{
int ret;
@@ -561,17 +561,14 @@ void regcache_cache_bypass(struct regmap *map, bool enable)
}
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
-bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
+void regcache_set_val(struct regmap *map, void *base, unsigned int idx,
unsigned int val)
{
- if (regcache_get_val(map, base, idx) == val)
- return true;
-
/* Use device native format if possible */
if (map->format.format_val) {
map->format.format_val(base + (map->cache_word_size * idx),
val, 0);
- return false;
+ return;
}
switch (map->cache_word_size) {
@@ -604,7 +601,6 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
default:
BUG();
}
- return false;
}
unsigned int regcache_get_val(struct regmap *map, const void *base,
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index c491fabe3617..f36027591e1a 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -636,6 +636,17 @@ void regmap_debugfs_init(struct regmap *map)
&regmap_cache_bypass_fops);
}
+ /*
+ * This could interfere with driver operation. Therefore, don't provide
+ * any real compile time configuration option for this feature. One will
+ * have to modify the source code directly in order to use it.
+ */
+#undef REGMAP_ALLOW_FORCE_WRITE_FIELD_DEBUGFS
+#ifdef REGMAP_ALLOW_FORCE_WRITE_FIELD_DEBUGFS
+ debugfs_create_bool("force_write_field", 0600, map->debugfs,
+ &map->force_write_field);
+#endif
+
next = rb_first(&map->range_tree);
while (next) {
range_node = rb_entry(next, struct regmap_range_node, node);
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index b99bb2369fff..ced0dcf86e0b 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -30,9 +30,6 @@ struct regmap_irq_chip_data {
int irq;
int wake_count;
- unsigned int mask_base;
- unsigned int unmask_base;
-
void *status_reg_buf;
unsigned int *main_status_buf;
unsigned int *status_buf;
@@ -41,7 +38,6 @@ struct regmap_irq_chip_data {
unsigned int *wake_buf;
unsigned int *type_buf;
unsigned int *type_buf_def;
- unsigned int **virt_buf;
unsigned int **config_buf;
unsigned int irq_reg_stride;
@@ -114,25 +110,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* suppress pointless writes.
*/
for (i = 0; i < d->chip->num_regs; i++) {
- if (d->mask_base) {
- if (d->chip->handle_mask_sync)
- d->chip->handle_mask_sync(d->map, i,
- d->mask_buf_def[i],
- d->mask_buf[i],
- d->chip->irq_drv_data);
- else {
- reg = d->get_irq_reg(d, d->mask_base, i);
- ret = regmap_update_bits(d->map, reg,
- d->mask_buf_def[i],
- d->mask_buf[i]);
- if (ret)
- dev_err(d->map->dev, "Failed to sync masks in %x\n",
- reg);
- }
+ if (d->chip->handle_mask_sync)
+ d->chip->handle_mask_sync(i, d->mask_buf_def[i],
+ d->mask_buf[i],
+ d->chip->irq_drv_data);
+
+ if (d->chip->mask_base && !d->chip->handle_mask_sync) {
+ reg = d->get_irq_reg(d, d->chip->mask_base, i);
+ ret = regmap_update_bits(d->map, reg,
+ d->mask_buf_def[i],
+ d->mask_buf[i]);
+ if (ret)
+ dev_err(d->map->dev, "Failed to sync masks in %x\n", reg);
}
- if (d->unmask_base) {
- reg = d->get_irq_reg(d, d->unmask_base, i);
+ if (d->chip->unmask_base && !d->chip->handle_mask_sync) {
+ reg = d->get_irq_reg(d, d->chip->unmask_base, i);
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret)
@@ -183,34 +176,6 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
}
}
- /* Don't update the type bits if we're using mask bits for irq type. */
- if (!d->chip->type_in_mask) {
- for (i = 0; i < d->chip->num_type_reg; i++) {
- if (!d->type_buf_def[i])
- continue;
- reg = d->get_irq_reg(d, d->chip->type_base, i);
- ret = regmap_update_bits(d->map, reg,
- d->type_buf_def[i], d->type_buf[i]);
- if (ret != 0)
- dev_err(d->map->dev, "Failed to sync type in %x\n",
- reg);
- }
- }
-
- if (d->chip->num_virt_regs) {
- for (i = 0; i < d->chip->num_virt_regs; i++) {
- for (j = 0; j < d->chip->num_regs; j++) {
- reg = d->get_irq_reg(d, d->chip->virt_reg_base[i],
- j);
- ret = regmap_write(map, reg, d->virt_buf[i][j]);
- if (ret != 0)
- dev_err(d->map->dev,
- "Failed to write virt 0x%x: %d\n",
- reg, ret);
- }
- }
- }
-
for (i = 0; i < d->chip->num_config_bases; i++) {
for (j = 0; j < d->chip->num_config_regs; j++) {
reg = d->get_irq_reg(d, d->chip->config_base[i], j);
@@ -289,41 +254,9 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
reg = t->type_reg_offset / map->reg_stride;
- if (t->type_reg_mask)
- d->type_buf[reg] &= ~t->type_reg_mask;
- else
- d->type_buf[reg] &= ~(t->type_falling_val |
- t->type_rising_val |
- t->type_level_low_val |
- t->type_level_high_val);
- switch (type) {
- case IRQ_TYPE_EDGE_FALLING:
- d->type_buf[reg] |= t->type_falling_val;
- break;
-
- case IRQ_TYPE_EDGE_RISING:
- d->type_buf[reg] |= t->type_rising_val;
- break;
-
- case IRQ_TYPE_EDGE_BOTH:
- d->type_buf[reg] |= (t->type_falling_val |
- t->type_rising_val);
- break;
-
- case IRQ_TYPE_LEVEL_HIGH:
- d->type_buf[reg] |= t->type_level_high_val;
- break;
-
- case IRQ_TYPE_LEVEL_LOW:
- d->type_buf[reg] |= t->type_level_low_val;
- break;
- default:
- return -EINVAL;
- }
-
- if (d->chip->set_type_virt) {
- ret = d->chip->set_type_virt(d->virt_buf, type, data->hwirq,
- reg);
+ if (d->chip->type_in_mask) {
+ ret = regmap_irq_set_type_config_simple(&d->type_buf, type,
+ irq_data, reg, d->chip->irq_drv_data);
if (ret)
return ret;
}
@@ -390,15 +323,8 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
unsigned int offset = subreg->offset[i];
unsigned int index = offset / map->reg_stride;
- if (chip->not_fixed_stride)
- ret = regmap_read(map,
- chip->status_base + offset,
- &data->status_buf[b]);
- else
- ret = regmap_read(map,
- chip->status_base + offset,
- &data->status_buf[index]);
-
+ ret = regmap_read(map, chip->status_base + offset,
+ &data->status_buf[index]);
if (ret)
break;
}
@@ -453,17 +379,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
* sake of simplicity. and add bulk reads only if needed
*/
for (i = 0; i < chip->num_main_regs; i++) {
- /*
- * For not_fixed_stride, don't use ->get_irq_reg().
- * It would produce an incorrect result.
- */
- if (data->chip->not_fixed_stride)
- reg = chip->main_status +
- i * map->reg_stride * data->irq_reg_stride;
- else
- reg = data->get_irq_reg(data,
- chip->main_status, i);
-
+ reg = data->get_irq_reg(data, chip->main_status, i);
ret = regmap_read(map, reg, &data->main_status_buf[i]);
if (ret) {
dev_err(map->dev,
@@ -586,12 +502,12 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
}
exit:
- if (chip->runtime_pm)
- pm_runtime_put(map->dev);
-
if (chip->handle_post_irq)
chip->handle_post_irq(chip->irq_drv_data);
+ if (chip->runtime_pm)
+ pm_runtime_put(map->dev);
+
if (handled)
return IRQ_HANDLED;
else
@@ -629,20 +545,8 @@ static const struct irq_domain_ops regmap_domain_ops = {
unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data,
unsigned int base, int index)
{
- const struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
- /*
- * FIXME: This is for backward compatibility and should be removed
- * when not_fixed_stride is dropped (it's only used by qcom-pm8008).
- */
- if (chip->not_fixed_stride && chip->sub_reg_offsets) {
- struct regmap_irq_sub_irq_map *subreg;
-
- subreg = &chip->sub_reg_offsets[0];
- return base + subreg->offset[0];
- }
-
return base + index * map->reg_stride * data->irq_reg_stride;
}
EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear);
@@ -730,8 +634,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
struct regmap_irq_chip_data *d;
int i;
int ret = -ENOMEM;
- int num_type_reg;
- int num_regs;
u32 reg;
if (chip->num_regs <= 0)
@@ -740,6 +642,9 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (chip->clear_on_unmask && (chip->ack_base || chip->use_ack))
return -EINVAL;
+ if (chip->mask_base && chip->unmask_base && !chip->mask_unmask_non_inverted)
+ return -EINVAL;
+
for (i = 0; i < chip->num_irqs; i++) {
if (chip->irqs[i].reg_offset % map->reg_stride)
return -EINVAL;
@@ -748,20 +653,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
return -EINVAL;
}
- if (chip->not_fixed_stride) {
- dev_warn(map->dev, "not_fixed_stride is deprecated; use ->get_irq_reg() instead");
-
- for (i = 0; i < chip->num_regs; i++)
- if (chip->sub_reg_offsets[i].num_regs != 1)
- return -EINVAL;
- }
-
- if (chip->num_type_reg)
- dev_warn(map->dev, "type registers are deprecated; use config registers instead");
-
- if (chip->num_virt_regs || chip->virt_reg_base || chip->set_type_virt)
- dev_warn(map->dev, "virtual registers are deprecated; use config registers instead");
-
if (irq_base) {
irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
if (irq_base < 0) {
@@ -806,43 +697,17 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
goto err_alloc;
}
- /*
- * Use num_config_regs if defined, otherwise fall back to num_type_reg
- * to maintain backward compatibility.
- */
- num_type_reg = chip->num_config_regs ? chip->num_config_regs
- : chip->num_type_reg;
- num_regs = chip->type_in_mask ? chip->num_regs : num_type_reg;
- if (num_regs) {
- d->type_buf_def = kcalloc(num_regs,
+ if (chip->type_in_mask) {
+ d->type_buf_def = kcalloc(chip->num_regs,
sizeof(*d->type_buf_def), GFP_KERNEL);
if (!d->type_buf_def)
goto err_alloc;
- d->type_buf = kcalloc(num_regs, sizeof(*d->type_buf),
- GFP_KERNEL);
+ d->type_buf = kcalloc(chip->num_regs, sizeof(*d->type_buf), GFP_KERNEL);
if (!d->type_buf)
goto err_alloc;
}
- if (chip->num_virt_regs) {
- /*
- * Create virt_buf[chip->num_extra_config_regs][chip->num_regs]
- */
- d->virt_buf = kcalloc(chip->num_virt_regs, sizeof(*d->virt_buf),
- GFP_KERNEL);
- if (!d->virt_buf)
- goto err_alloc;
-
- for (i = 0; i < chip->num_virt_regs; i++) {
- d->virt_buf[i] = kcalloc(chip->num_regs,
- sizeof(**d->virt_buf),
- GFP_KERNEL);
- if (!d->virt_buf[i])
- goto err_alloc;
- }
- }
-
if (chip->num_config_bases && chip->num_config_regs) {
/*
* Create config_buf[num_config_bases][num_config_regs]
@@ -868,28 +733,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
d->chip = chip;
d->irq_base = irq_base;
- if (chip->mask_base && chip->unmask_base &&
- !chip->mask_unmask_non_inverted) {
- /*
- * Chips that specify both mask_base and unmask_base used to
- * get inverted mask behavior by default, with no way to ask
- * for the normal, non-inverted behavior. This "inverted by
- * default" behavior is deprecated, but we have to support it
- * until existing drivers have been fixed.
- *
- * Existing drivers should be updated by swapping mask_base
- * and unmask_base and setting mask_unmask_non_inverted=true.
- * New drivers should always set the flag.
- */
- dev_warn(map->dev, "mask_base and unmask_base are inverted, please fix it");
-
- d->mask_base = chip->unmask_base;
- d->unmask_base = chip->mask_base;
- } else {
- d->mask_base = chip->mask_base;
- d->unmask_base = chip->unmask_base;
- }
-
if (chip->irq_reg_stride)
d->irq_reg_stride = chip->irq_reg_stride;
else
@@ -918,29 +761,28 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
for (i = 0; i < chip->num_regs; i++) {
d->mask_buf[i] = d->mask_buf_def[i];
- if (d->mask_base) {
- if (chip->handle_mask_sync) {
- ret = chip->handle_mask_sync(d->map, i,
- d->mask_buf_def[i],
- d->mask_buf[i],
- chip->irq_drv_data);
- if (ret)
- goto err_alloc;
- } else {
- reg = d->get_irq_reg(d, d->mask_base, i);
- ret = regmap_update_bits(d->map, reg,
- d->mask_buf_def[i],
- d->mask_buf[i]);
- if (ret) {
- dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
- reg, ret);
- goto err_alloc;
- }
+ if (chip->handle_mask_sync) {
+ ret = chip->handle_mask_sync(i, d->mask_buf_def[i],
+ d->mask_buf[i],
+ chip->irq_drv_data);
+ if (ret)
+ goto err_alloc;
+ }
+
+ if (chip->mask_base && !chip->handle_mask_sync) {
+ reg = d->get_irq_reg(d, chip->mask_base, i);
+ ret = regmap_update_bits(d->map, reg,
+ d->mask_buf_def[i],
+ d->mask_buf[i]);
+ if (ret) {
+ dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
+ reg, ret);
+ goto err_alloc;
}
}
- if (d->unmask_base) {
- reg = d->get_irq_reg(d, d->unmask_base, i);
+ if (chip->unmask_base && !chip->handle_mask_sync) {
+ reg = d->get_irq_reg(d, chip->unmask_base, i);
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret) {
@@ -1014,20 +856,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
}
}
- if (chip->num_type_reg && !chip->type_in_mask) {
- for (i = 0; i < chip->num_type_reg; ++i) {
- reg = d->get_irq_reg(d, d->chip->type_base, i);
-
- ret = regmap_read(map, reg, &d->type_buf_def[i]);
-
- if (ret) {
- dev_err(map->dev, "Failed to get type defaults at 0x%x: %d\n",
- reg, ret);
- goto err_alloc;
- }
- }
- }
-
if (irq_base)
d->domain = irq_domain_create_legacy(fwnode, chip->num_irqs,
irq_base, 0,
@@ -1064,11 +892,6 @@ err_alloc:
kfree(d->mask_buf);
kfree(d->status_buf);
kfree(d->status_reg_buf);
- if (d->virt_buf) {
- for (i = 0; i < chip->num_virt_regs; i++)
- kfree(d->virt_buf[i]);
- kfree(d->virt_buf);
- }
if (d->config_buf) {
for (i = 0; i < chip->num_config_bases; i++)
kfree(d->config_buf[i]);
diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c
index f76d41688134..24257aa9004d 100644
--- a/drivers/base/regmap/regmap-kunit.c
+++ b/drivers/base/regmap/regmap-kunit.c
@@ -92,6 +92,11 @@ static struct regmap *gen_regmap(struct regmap_config *config,
return ret;
}
+static bool reg_5_false(struct device *context, unsigned int reg)
+{
+ return reg != 5;
+}
+
static void basic_read_write(struct kunit *test)
{
struct regcache_types *t = (struct regcache_types *)test->param_value;
@@ -191,6 +196,81 @@ static void bulk_read(struct kunit *test)
regmap_exit(map);
}
+static void write_readonly(struct kunit *test)
+{
+ struct regcache_types *t = (struct regcache_types *)test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = test_regmap_config;
+ config.cache_type = t->type;
+ config.num_reg_defaults = BLOCK_TEST_SIZE;
+ config.writeable_reg = reg_5_false;
+
+ map = gen_regmap(&config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[i] = false;
+
+ /* Change the value of all registers, readonly should fail */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, i != 5, regmap_write(map, i, val) == 0);
+
+ /* Did that match what we see on the device? */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, i != 5, data->written[i]);
+
+ regmap_exit(map);
+}
+
+static void read_writeonly(struct kunit *test)
+{
+ struct regcache_types *t = (struct regcache_types *)test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = test_regmap_config;
+ config.cache_type = t->type;
+ config.readable_reg = reg_5_false;
+
+ map = gen_regmap(&config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->read[i] = false;
+
+ /*
+ * Try to read all the registers, the writeonly one should
+ * fail if we aren't using the flat cache.
+ */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ if (t->type != REGCACHE_FLAT) {
+ KUNIT_EXPECT_EQ(test, i != 5,
+ regmap_read(map, i, &val) == 0);
+ } else {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &val));
+ }
+ }
+
+ /* Did we trigger a hardware access? */
+ KUNIT_EXPECT_FALSE(test, data->read[5]);
+
+ regmap_exit(map);
+}
+
static void reg_defaults(struct kunit *test)
{
struct regcache_types *t = (struct regcache_types *)test->param_value;
@@ -609,6 +689,47 @@ static void cache_sync_defaults(struct kunit *test)
regmap_exit(map);
}
+static void cache_sync_readonly(struct kunit *test)
+{
+ struct regcache_types *t = (struct regcache_types *)test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = test_regmap_config;
+ config.cache_type = t->type;
+ config.writeable_reg = reg_5_false;
+
+ map = gen_regmap(&config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Read all registers to fill the cache */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &val));
+
+ /* Change the value of all registers, readonly should fail */
+ get_random_bytes(&val, sizeof(val));
+ regcache_cache_only(map, true);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, i != 5, regmap_write(map, i, val) == 0);
+ regcache_cache_only(map, false);
+
+ /* Resync */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[i] = false;
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+
+ /* Did that match what we see on the device? */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, i != 5, data->written[i]);
+
+ regmap_exit(map);
+}
+
static void cache_sync_patch(struct kunit *test)
{
struct regcache_types *t = (struct regcache_types *)test->param_value;
@@ -712,10 +833,333 @@ static void cache_drop(struct kunit *test)
regmap_exit(map);
}
+struct raw_test_types {
+ const char *name;
+
+ enum regcache_type cache_type;
+ enum regmap_endian val_endian;
+};
+
+static void raw_to_desc(const struct raw_test_types *t, char *desc)
+{
+ strcpy(desc, t->name);
+}
+
+static const struct raw_test_types raw_types_list[] = {
+ { "none-little", REGCACHE_NONE, REGMAP_ENDIAN_LITTLE },
+ { "none-big", REGCACHE_NONE, REGMAP_ENDIAN_BIG },
+ { "flat-little", REGCACHE_FLAT, REGMAP_ENDIAN_LITTLE },
+ { "flat-big", REGCACHE_FLAT, REGMAP_ENDIAN_BIG },
+ { "rbtree-little", REGCACHE_RBTREE, REGMAP_ENDIAN_LITTLE },
+ { "rbtree-big", REGCACHE_RBTREE, REGMAP_ENDIAN_BIG },
+ { "maple-little", REGCACHE_MAPLE, REGMAP_ENDIAN_LITTLE },
+ { "maple-big", REGCACHE_MAPLE, REGMAP_ENDIAN_BIG },
+};
+
+KUNIT_ARRAY_PARAM(raw_test_types, raw_types_list, raw_to_desc);
+
+static const struct raw_test_types raw_cache_types_list[] = {
+ { "flat-little", REGCACHE_FLAT, REGMAP_ENDIAN_LITTLE },
+ { "flat-big", REGCACHE_FLAT, REGMAP_ENDIAN_BIG },
+ { "rbtree-little", REGCACHE_RBTREE, REGMAP_ENDIAN_LITTLE },
+ { "rbtree-big", REGCACHE_RBTREE, REGMAP_ENDIAN_BIG },
+ { "maple-little", REGCACHE_MAPLE, REGMAP_ENDIAN_LITTLE },
+ { "maple-big", REGCACHE_MAPLE, REGMAP_ENDIAN_BIG },
+};
+
+KUNIT_ARRAY_PARAM(raw_test_cache_types, raw_cache_types_list, raw_to_desc);
+
+static const struct regmap_config raw_regmap_config = {
+ .max_register = BLOCK_TEST_SIZE,
+
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .reg_bits = 16,
+ .val_bits = 16,
+};
+
+static struct regmap *gen_raw_regmap(struct regmap_config *config,
+ struct raw_test_types *test_type,
+ struct regmap_ram_data **data)
+{
+ u16 *buf;
+ struct regmap *ret;
+ size_t size = (config->max_register + 1) * config->reg_bits / 8;
+ int i;
+ struct reg_default *defaults;
+
+ config->cache_type = test_type->cache_type;
+ config->val_format_endian = test_type->val_endian;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ get_random_bytes(buf, size);
+
+ *data = kzalloc(sizeof(**data), GFP_KERNEL);
+ if (!(*data))
+ return ERR_PTR(-ENOMEM);
+ (*data)->vals = (void *)buf;
+
+ config->num_reg_defaults = config->max_register + 1;
+ defaults = kcalloc(config->num_reg_defaults,
+ sizeof(struct reg_default),
+ GFP_KERNEL);
+ if (!defaults)
+ return ERR_PTR(-ENOMEM);
+ config->reg_defaults = defaults;
+
+ for (i = 0; i < config->num_reg_defaults; i++) {
+ defaults[i].reg = i;
+ switch (test_type->val_endian) {
+ case REGMAP_ENDIAN_LITTLE:
+ defaults[i].def = le16_to_cpu(buf[i]);
+ break;
+ case REGMAP_ENDIAN_BIG:
+ defaults[i].def = be16_to_cpu(buf[i]);
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
+ /*
+ * We use the defaults in the tests but they don't make sense
+ * to the core if there's no cache.
+ */
+ if (config->cache_type == REGCACHE_NONE)
+ config->num_reg_defaults = 0;
+
+ ret = regmap_init_raw_ram(config, *data);
+ if (IS_ERR(ret)) {
+ kfree(buf);
+ kfree(*data);
+ }
+
+ return ret;
+}
+
+static void raw_read_defaults_single(struct kunit *test)
+{
+ struct raw_test_types *t = (struct raw_test_types *)test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int rval;
+ int i;
+
+ config = raw_regmap_config;
+
+ map = gen_raw_regmap(&config, t, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Check that we can read the defaults via the API */
+ for (i = 0; i < config.max_register + 1; i++) {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval));
+ KUNIT_EXPECT_EQ(test, config.reg_defaults[i].def, rval);
+ }
+
+ regmap_exit(map);
+}
+
+static void raw_read_defaults(struct kunit *test)
+{
+ struct raw_test_types *t = (struct raw_test_types *)test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ u16 *rval;
+ u16 def;
+ size_t val_len;
+ int i;
+
+ config = raw_regmap_config;
+
+ map = gen_raw_regmap(&config, t, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ val_len = sizeof(*rval) * (config.max_register + 1);
+ rval = kmalloc(val_len, GFP_KERNEL);
+ KUNIT_ASSERT_TRUE(test, rval != NULL);
+ if (!rval)
+ return;
+
+ /* Check that we can read the defaults via the API */
+ KUNIT_EXPECT_EQ(test, 0, regmap_raw_read(map, 0, rval, val_len));
+ for (i = 0; i < config.max_register + 1; i++) {
+ def = config.reg_defaults[i].def;
+ if (config.val_format_endian == REGMAP_ENDIAN_BIG) {
+ KUNIT_EXPECT_EQ(test, def, be16_to_cpu(rval[i]));
+ } else {
+ KUNIT_EXPECT_EQ(test, def, le16_to_cpu(rval[i]));
+ }
+ }
+
+ kfree(rval);
+ regmap_exit(map);
+}
+
+static void raw_write_read_single(struct kunit *test)
+{
+ struct raw_test_types *t = (struct raw_test_types *)test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ u16 val;
+ unsigned int rval;
+
+ config = raw_regmap_config;
+
+ map = gen_raw_regmap(&config, t, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* If we write a value to a register we can read it back */
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val));
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval));
+ KUNIT_EXPECT_EQ(test, val, rval);
+
+ regmap_exit(map);
+}
+
+static void raw_write(struct kunit *test)
+{
+ struct raw_test_types *t = (struct raw_test_types *)test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ u16 *hw_buf;
+ u16 val[2];
+ unsigned int rval;
+ int i;
+
+ config = raw_regmap_config;
+
+ map = gen_raw_regmap(&config, t, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ hw_buf = (u16 *)data->vals;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* Do a raw write */
+ KUNIT_EXPECT_EQ(test, 0, regmap_raw_write(map, 2, val, sizeof(val)));
+
+ /* We should read back the new values, and defaults for the rest */
+ for (i = 0; i < config.max_register + 1; i++) {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval));
+
+ switch (i) {
+ case 2:
+ case 3:
+ if (config.val_format_endian == REGMAP_ENDIAN_BIG) {
+ KUNIT_EXPECT_EQ(test, rval,
+ be16_to_cpu(val[i % 2]));
+ } else {
+ KUNIT_EXPECT_EQ(test, rval,
+ le16_to_cpu(val[i % 2]));
+ }
+ break;
+ default:
+ KUNIT_EXPECT_EQ(test, config.reg_defaults[i].def, rval);
+ break;
+ }
+ }
+
+ /* The values should appear in the "hardware" */
+ KUNIT_EXPECT_MEMEQ(test, &hw_buf[2], val, sizeof(val));
+
+ regmap_exit(map);
+}
+
+static void raw_sync(struct kunit *test)
+{
+ struct raw_test_types *t = (struct raw_test_types *)test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ u16 val[2];
+ u16 *hw_buf;
+ unsigned int rval;
+ int i;
+
+ config = raw_regmap_config;
+
+ map = gen_raw_regmap(&config, t, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ hw_buf = (u16 *)data->vals;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* Do a regular write and a raw write in cache only mode */
+ regcache_cache_only(map, true);
+ KUNIT_EXPECT_EQ(test, 0, regmap_raw_write(map, 2, val, sizeof(val)));
+ if (config.val_format_endian == REGMAP_ENDIAN_BIG)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 6,
+ be16_to_cpu(val[0])));
+ else
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 6,
+ le16_to_cpu(val[0])));
+
+ /* We should read back the new values, and defaults for the rest */
+ for (i = 0; i < config.max_register + 1; i++) {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval));
+
+ switch (i) {
+ case 2:
+ case 3:
+ case 6:
+ if (config.val_format_endian == REGMAP_ENDIAN_BIG) {
+ KUNIT_EXPECT_EQ(test, rval,
+ be16_to_cpu(val[i % 2]));
+ } else {
+ KUNIT_EXPECT_EQ(test, rval,
+ le16_to_cpu(val[i % 2]));
+ }
+ break;
+ default:
+ KUNIT_EXPECT_EQ(test, config.reg_defaults[i].def, rval);
+ break;
+ }
+ }
+
+ /* The values should not appear in the "hardware" */
+ KUNIT_EXPECT_MEMNEQ(test, &hw_buf[2], val, sizeof(val));
+ KUNIT_EXPECT_MEMNEQ(test, &hw_buf[6], val, sizeof(u16));
+
+ for (i = 0; i < config.max_register + 1; i++)
+ data->written[i] = false;
+
+ /* Do the sync */
+ regcache_cache_only(map, false);
+ regcache_mark_dirty(map);
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+
+ /* The values should now appear in the "hardware" */
+ KUNIT_EXPECT_MEMEQ(test, &hw_buf[2], val, sizeof(val));
+ KUNIT_EXPECT_MEMEQ(test, &hw_buf[6], val, sizeof(u16));
+
+ regmap_exit(map);
+}
+
static struct kunit_case regmap_test_cases[] = {
KUNIT_CASE_PARAM(basic_read_write, regcache_types_gen_params),
KUNIT_CASE_PARAM(bulk_write, regcache_types_gen_params),
KUNIT_CASE_PARAM(bulk_read, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(write_readonly, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(read_writeonly, regcache_types_gen_params),
KUNIT_CASE_PARAM(reg_defaults, regcache_types_gen_params),
KUNIT_CASE_PARAM(reg_defaults_read_dev, regcache_types_gen_params),
KUNIT_CASE_PARAM(register_patch, regcache_types_gen_params),
@@ -725,8 +1169,15 @@ static struct kunit_case regmap_test_cases[] = {
KUNIT_CASE_PARAM(cache_bypass, real_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_sync, real_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_sync_defaults, real_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_sync_readonly, real_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_sync_patch, real_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_drop, sparse_cache_types_gen_params),
+
+ KUNIT_CASE_PARAM(raw_read_defaults_single, raw_test_types_gen_params),
+ KUNIT_CASE_PARAM(raw_read_defaults, raw_test_types_gen_params),
+ KUNIT_CASE_PARAM(raw_write_read_single, raw_test_types_gen_params),
+ KUNIT_CASE_PARAM(raw_write, raw_test_types_gen_params),
+ KUNIT_CASE_PARAM(raw_sync, raw_test_cache_types_gen_params),
{}
};
diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c
index 3ccdd86a97e7..8132b5c101c4 100644
--- a/drivers/base/regmap/regmap-mmio.c
+++ b/drivers/base/regmap/regmap-mmio.c
@@ -448,7 +448,7 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
if (min_stride < 0)
return ERR_PTR(min_stride);
- if (config->reg_stride < min_stride)
+ if (config->reg_stride && config->reg_stride < min_stride)
return ERR_PTR(-EINVAL);
if (config->use_relaxed_mmio && config->io_port)
diff --git a/drivers/base/regmap/regmap-raw-ram.c b/drivers/base/regmap/regmap-raw-ram.c
new file mode 100644
index 000000000000..c9b800885f3b
--- /dev/null
+++ b/drivers/base/regmap/regmap-raw-ram.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Register map access API - Memory region with raw access
+//
+// This is intended for testing only
+//
+// Copyright (c) 2023, Arm Ltd
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/swab.h>
+
+#include "internal.h"
+
+static unsigned int decode_reg(enum regmap_endian endian, const void *reg)
+{
+ const u16 *r = reg;
+
+ if (endian == REGMAP_ENDIAN_BIG)
+ return be16_to_cpu(*r);
+ else
+ return le16_to_cpu(*r);
+}
+
+static int regmap_raw_ram_gather_write(void *context,
+ const void *reg, size_t reg_len,
+ const void *val, size_t val_len)
+{
+ struct regmap_ram_data *data = context;
+ unsigned int r;
+ u16 *our_buf = (u16 *)data->vals;
+ int i;
+
+ if (reg_len != 2)
+ return -EINVAL;
+ if (val_len % 2)
+ return -EINVAL;
+
+ r = decode_reg(data->reg_endian, reg);
+ memcpy(&our_buf[r], val, val_len);
+
+ for (i = 0; i < val_len / 2; i++)
+ data->written[r + i] = true;
+
+ return 0;
+}
+
+static int regmap_raw_ram_write(void *context, const void *data, size_t count)
+{
+ return regmap_raw_ram_gather_write(context, data, 2,
+ data + 2, count - 2);
+}
+
+static int regmap_raw_ram_read(void *context,
+ const void *reg, size_t reg_len,
+ void *val, size_t val_len)
+{
+ struct regmap_ram_data *data = context;
+ unsigned int r;
+ u16 *our_buf = (u16 *)data->vals;
+ int i;
+
+ if (reg_len != 2)
+ return -EINVAL;
+ if (val_len % 2)
+ return -EINVAL;
+
+ r = decode_reg(data->reg_endian, reg);
+ memcpy(val, &our_buf[r], val_len);
+
+ for (i = 0; i < val_len / 2; i++)
+ data->read[r + i] = true;
+
+ return 0;
+}
+
+static void regmap_raw_ram_free_context(void *context)
+{
+ struct regmap_ram_data *data = context;
+
+ kfree(data->vals);
+ kfree(data->read);
+ kfree(data->written);
+ kfree(data);
+}
+
+static const struct regmap_bus regmap_raw_ram = {
+ .fast_io = true,
+ .write = regmap_raw_ram_write,
+ .gather_write = regmap_raw_ram_gather_write,
+ .read = regmap_raw_ram_read,
+ .free_context = regmap_raw_ram_free_context,
+};
+
+struct regmap *__regmap_init_raw_ram(const struct regmap_config *config,
+ struct regmap_ram_data *data,
+ struct lock_class_key *lock_key,
+ const char *lock_name)
+{
+ struct regmap *map;
+
+ if (config->reg_bits != 16)
+ return ERR_PTR(-EINVAL);
+
+ if (!config->max_register) {
+ pr_crit("No max_register specified for RAM regmap\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ data->read = kcalloc(sizeof(bool), config->max_register + 1,
+ GFP_KERNEL);
+ if (!data->read)
+ return ERR_PTR(-ENOMEM);
+
+ data->written = kcalloc(sizeof(bool), config->max_register + 1,
+ GFP_KERNEL);
+ if (!data->written)
+ return ERR_PTR(-ENOMEM);
+
+ data->reg_endian = config->reg_format_endian;
+
+ map = __regmap_init(NULL, &regmap_raw_ram, data, config,
+ lock_key, lock_name);
+
+ return map;
+}
+EXPORT_SYMBOL_GPL(__regmap_init_raw_ram);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index fa2d3fba6ac9..89a7f1c459c1 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -2983,6 +2983,11 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
size_t chunk_count, chunk_bytes;
size_t chunk_regs = val_count;
+ if (!map->cache_bypass && map->cache_only) {
+ ret = -EBUSY;
+ goto out;
+ }
+
if (!map->read) {
ret = -ENOTSUPP;
goto out;
@@ -3078,18 +3083,19 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
goto out_unlock;
}
+ /*
+ * We have not defined the FIFO semantics for cache, as the
+ * cache is just one value deep. Should we return the last
+ * written value? Just avoid this by always reading the FIFO
+ * even when using cache. Cache only will not work.
+ */
+ if (!map->cache_bypass && map->cache_only) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
/* Use the accelerated operation if we can */
if (map->bus->reg_noinc_read) {
- /*
- * We have not defined the FIFO semantics for cache, as the
- * cache is just one value deep. Should we return the last
- * written value? Just avoid this by always reading the FIFO
- * even when using cache. Cache only will not work.
- */
- if (map->cache_only) {
- ret = -EBUSY;
- goto out_unlock;
- }
ret = regmap_noinc_readwrite(map, reg, val, val_len, false);
goto out_unlock;
}
@@ -3273,7 +3279,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
tmp = orig & ~mask;
tmp |= val & mask;
- if (force_write || (tmp != orig)) {
+ if (force_write || (tmp != orig) || map->force_write_field) {
ret = _regmap_write(map, reg, tmp);
if (ret == 0 && change)
*change = true;