Commit f5468bec authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'regmap-v6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap

Pull regmap updates from Mark Brown:
 "This is a much bigger change for regmap than is normal, the main
  things being the addition of some KUnit coverage and a maple tree
  based register cache which longer term is likely to replace the rbtree
  cache except possibly for very small register maps.

  While it's complete overkill for most applications the code for maple
  trees is there and there are some larger, sparser devices where the
  data structure is a better fit.

  The maple tree support is still a work in progress but already useful,
  there's some conversions of drivers ready to go after the merge
  window.

  Summary:

   - Support for shifting register addresses up as well as down, there's
     a use cases with memory mapped MDIO.

   - Refactoring of the type configuration in regmap-irq to allow access
     to driver data in the handler, needed by some GPIO devices.

   - Some initial KUnit coverage, the bulk of the driver facing API is
     covered but there's holes and things like the data marshalling for
     bytestream buses are just not covered in the slightest.

   - Removal of the compressed cache type, it had zero users and was
     getting in the way of KUnit.

   - Addition of a maple tree based register cache, there's more work to
     do but it's already useful for some devices with a flatter data
     structure than rbtree and getting to use all the optimisation work
     Liam is doing"

* tag 'regmap-v6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: allow upshifting register addresses before performing operations
  regmap: Pass irq_drv_data as a parameter for set_type_config()
  regmap: Use mas_walk() instead of mas_find()
  regmap: Fix double unlock in the maple cache
  regmap: Add maple tree based register cache
  regmap: Factor out single value register syncing
  regmap: Add some basic kunit tests
  regmap: Add RAM backed register map
  regmap: Removed compressed cache support
  regmap: Support paging for buses with reg_read()/reg_write()
  regmap: Clarify error for unknown cache types
  regmap: Handle sparse caches in the default sync
  regmap: add a helper to translate the register address
  regmap: cache: Silence checkpatch warning
  regmap: cache: Return error in cache sync operations for REGCACHE_NONE
  regmap-irq: Place kernel doc of struct regmap_irq_chip in order
  regmap-irq: Add no_status support
  regmap: sdw: Remove 8-bit value size restriction
  regmap: sdw: Update misleading comment
parents 088e0c18 4a670ac3
......@@ -9,10 +9,12 @@ config REGMAP
select MDIO_BUS if REGMAP_MDIO
bool
config REGCACHE_COMPRESSED
select LZO_COMPRESS
select LZO_DECOMPRESS
bool
config REGMAP_KUNIT
tristate "KUnit tests for regmap"
depends on KUNIT
default KUNIT_ALL_TESTS
select REGMAP
select REGMAP_RAM
config REGMAP_AC97
tristate
......@@ -46,6 +48,9 @@ config REGMAP_MMIO
config REGMAP_IRQ
bool
config REGMAP_RAM
tristate
config REGMAP_SOUNDWIRE
tristate
depends on SOUNDWIRE
......
......@@ -3,11 +3,12 @@
CFLAGS_regmap.o := -I$(src)
obj-$(CONFIG_REGMAP) += regmap.o regcache.o
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o
obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o regcache-maple.o
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_SLIMBUS) += regmap-slimbus.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
......
......@@ -31,8 +31,8 @@ struct regmap_format {
size_t buf_size;
size_t reg_bytes;
size_t pad_bytes;
size_t reg_downshift;
size_t val_bytes;
s8 reg_shift;
void (*format_write)(struct regmap *map,
unsigned int reg, unsigned int val);
void (*format_reg)(void *buf, unsigned int reg, unsigned int shift);
......@@ -270,6 +270,7 @@ unsigned int regcache_get_val(struct regmap *map, const void *base,
bool 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);
int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool noinc);
......@@ -281,7 +282,7 @@ enum regmap_endian regmap_get_val_endian(struct device *dev,
const struct regmap_config *config);
extern struct regcache_ops regcache_rbtree_ops;
extern struct regcache_ops regcache_lzo_ops;
extern struct regcache_ops regcache_maple_ops;
extern struct regcache_ops regcache_flat_ops;
static inline const char *regmap_name(const struct regmap *map)
......@@ -307,4 +308,23 @@ static inline unsigned int regcache_get_index_by_order(const struct regmap *map,
return reg >> map->reg_stride_order;
}
struct regmap_ram_data {
unsigned int *vals; /* Allocatd by caller */
bool *read;
bool *written;
};
/*
* Create a test register map with data stored in RAM, not intended
* for practical use.
*/
struct regmap *__regmap_init_ram(const struct regmap_config *config,
struct regmap_ram_data *data,
struct lock_class_key *lock_key,
const char *lock_name);
#define regmap_init_ram(config, data) \
__regmap_lockdep_wrapper(__regmap_init_ram, #config, config, data)
#endif
// SPDX-License-Identifier: GPL-2.0
//
// Register cache access API - LZO caching support
//
// Copyright 2011 Wolfson Microelectronics plc
//
// Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
#include <linux/device.h>
#include <linux/lzo.h>
#include <linux/slab.h>
#include "internal.h"
static int regcache_lzo_exit(struct regmap *map);
struct regcache_lzo_ctx {
void *wmem;
void *dst;
const void *src;
size_t src_len;
size_t dst_len;
size_t decompressed_size;
unsigned long *sync_bmp;
int sync_bmp_nbits;
};
#define LZO_BLOCK_NUM 8
static int regcache_lzo_block_count(struct regmap *map)
{
return LZO_BLOCK_NUM;
}
static int regcache_lzo_prepare(struct regcache_lzo_ctx *lzo_ctx)
{
lzo_ctx->wmem = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
if (!lzo_ctx->wmem)
return -ENOMEM;
return 0;
}
static int regcache_lzo_compress(struct regcache_lzo_ctx *lzo_ctx)
{
size_t compress_size;
int ret;
ret = lzo1x_1_compress(lzo_ctx->src, lzo_ctx->src_len,
lzo_ctx->dst, &compress_size, lzo_ctx->wmem);
if (ret != LZO_E_OK || compress_size > lzo_ctx->dst_len)
return -EINVAL;
lzo_ctx->dst_len = compress_size;
return 0;
}
static int regcache_lzo_decompress(struct regcache_lzo_ctx *lzo_ctx)
{
size_t dst_len;
int ret;
dst_len = lzo_ctx->dst_len;
ret = lzo1x_decompress_safe(lzo_ctx->src, lzo_ctx->src_len,
lzo_ctx->dst, &dst_len);
if (ret != LZO_E_OK || dst_len != lzo_ctx->dst_len)
return -EINVAL;
return 0;
}
static int regcache_lzo_compress_cache_block(struct regmap *map,
struct regcache_lzo_ctx *lzo_ctx)
{
int ret;
lzo_ctx->dst_len = lzo1x_worst_compress(PAGE_SIZE);
lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
if (!lzo_ctx->dst) {
lzo_ctx->dst_len = 0;
return -ENOMEM;
}
ret = regcache_lzo_compress(lzo_ctx);
if (ret < 0)
return ret;
return 0;
}
static int regcache_lzo_decompress_cache_block(struct regmap *map,
struct regcache_lzo_ctx *lzo_ctx)
{
int ret;
lzo_ctx->dst_len = lzo_ctx->decompressed_size;
lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
if (!lzo_ctx->dst) {
lzo_ctx->dst_len = 0;
return -ENOMEM;
}
ret = regcache_lzo_decompress(lzo_ctx);
if (ret < 0)
return ret;
return 0;
}
static inline int regcache_lzo_get_blkindex(struct regmap *map,
unsigned int reg)
{
return ((reg / map->reg_stride) * map->cache_word_size) /
DIV_ROUND_UP(map->cache_size_raw,
regcache_lzo_block_count(map));
}
static inline int regcache_lzo_get_blkpos(struct regmap *map,
unsigned int reg)
{
return (reg / map->reg_stride) %
(DIV_ROUND_UP(map->cache_size_raw,
regcache_lzo_block_count(map)) /
map->cache_word_size);
}
static inline int regcache_lzo_get_blksize(struct regmap *map)
{
return DIV_ROUND_UP(map->cache_size_raw,
regcache_lzo_block_count(map));
}
static int regcache_lzo_init(struct regmap *map)
{
struct regcache_lzo_ctx **lzo_blocks;
size_t bmp_size;
int ret, i, blksize, blkcount;
const char *p, *end;
unsigned long *sync_bmp;
ret = 0;
blkcount = regcache_lzo_block_count(map);
map->cache = kcalloc(blkcount, sizeof(*lzo_blocks),
GFP_KERNEL);
if (!map->cache)
return -ENOMEM;
lzo_blocks = map->cache;
/*
* allocate a bitmap to be used when syncing the cache with
* the hardware. Each time a register is modified, the corresponding
* bit is set in the bitmap, so we know that we have to sync
* that register.
*/
bmp_size = map->num_reg_defaults_raw;
sync_bmp = bitmap_zalloc(bmp_size, GFP_KERNEL);
if (!sync_bmp) {
ret = -ENOMEM;
goto err;
}
/* allocate the lzo blocks and initialize them */
for (i = 0; i < blkcount; i++) {
lzo_blocks[i] = kzalloc(sizeof **lzo_blocks,
GFP_KERNEL);
if (!lzo_blocks[i]) {
bitmap_free(sync_bmp);
ret = -ENOMEM;
goto err;
}
lzo_blocks[i]->sync_bmp = sync_bmp;
lzo_blocks[i]->sync_bmp_nbits = bmp_size;
/* alloc the working space for the compressed block */
ret = regcache_lzo_prepare(lzo_blocks[i]);
if (ret < 0)
goto err;
}
blksize = regcache_lzo_get_blksize(map);
p = map->reg_defaults_raw;
end = map->reg_defaults_raw + map->cache_size_raw;
/* compress the register map and fill the lzo blocks */
for (i = 0; i < blkcount; i++, p += blksize) {
lzo_blocks[i]->src = p;
if (p + blksize > end)
lzo_blocks[i]->src_len = end - p;
else
lzo_blocks[i]->src_len = blksize;
ret = regcache_lzo_compress_cache_block(map,
lzo_blocks[i]);
if (ret < 0)
goto err;
lzo_blocks[i]->decompressed_size =
lzo_blocks[i]->src_len;
}
return 0;
err:
regcache_lzo_exit(map);
return ret;
}
static int regcache_lzo_exit(struct regmap *map)
{
struct regcache_lzo_ctx **lzo_blocks;
int i, blkcount;
lzo_blocks = map->cache;
if (!lzo_blocks)
return 0;
blkcount = regcache_lzo_block_count(map);
/*
* the pointer to the bitmap used for syncing the cache
* is shared amongst all lzo_blocks. Ensure it is freed
* only once.
*/
if (lzo_blocks[0])
bitmap_free(lzo_blocks[0]->sync_bmp);
for (i = 0; i < blkcount; i++) {
if (lzo_blocks[i]) {
kfree(lzo_blocks[i]->wmem);
kfree(lzo_blocks[i]->dst);
}
/* each lzo_block is a pointer returned by kmalloc or NULL */
kfree(lzo_blocks[i]);
}
kfree(lzo_blocks);
map->cache = NULL;
return 0;
}
static int regcache_lzo_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
int ret, blkindex, blkpos;
size_t tmp_dst_len;
void *tmp_dst;
/* index of the compressed lzo block */
blkindex = regcache_lzo_get_blkindex(map, reg);
/* register index within the decompressed block */
blkpos = regcache_lzo_get_blkpos(map, reg);
lzo_blocks = map->cache;
lzo_block = lzo_blocks[blkindex];
/* save the pointer and length of the compressed block */
tmp_dst = lzo_block->dst;
tmp_dst_len = lzo_block->dst_len;
/* prepare the source to be the compressed block */
lzo_block->src = lzo_block->dst;
lzo_block->src_len = lzo_block->dst_len;
/* decompress the block */
ret = regcache_lzo_decompress_cache_block(map, lzo_block);
if (ret >= 0)
/* fetch the value from the cache */
*value = regcache_get_val(map, lzo_block->dst, blkpos);
kfree(lzo_block->dst);
/* restore the pointer and length of the compressed block */
lzo_block->dst = tmp_dst;
lzo_block->dst_len = tmp_dst_len;
return ret;
}
static int regcache_lzo_write(struct regmap *map,
unsigned int reg, unsigned int value)
{
struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
int ret, blkindex, blkpos;
size_t tmp_dst_len;
void *tmp_dst;
/* index of the compressed lzo block */
blkindex = regcache_lzo_get_blkindex(map, reg);
/* register index within the decompressed block */
blkpos = regcache_lzo_get_blkpos(map, reg);
lzo_blocks = map->cache;
lzo_block = lzo_blocks[blkindex];
/* save the pointer and length of the compressed block */
tmp_dst = lzo_block->dst;
tmp_dst_len = lzo_block->dst_len;
/* prepare the source to be the compressed block */
lzo_block->src = lzo_block->dst;
lzo_block->src_len = lzo_block->dst_len;
/* decompress the block */
ret = regcache_lzo_decompress_cache_block(map, lzo_block);
if (ret < 0) {
kfree(lzo_block->dst);
goto out;
}
/* write the new value to the cache */
if (regcache_set_val(map, lzo_block->dst, blkpos, value)) {
kfree(lzo_block->dst);
goto out;
}
/* prepare the source to be the decompressed block */
lzo_block->src = lzo_block->dst;
lzo_block->src_len = lzo_block->dst_len;
/* compress the block */
ret = regcache_lzo_compress_cache_block(map, lzo_block);
if (ret < 0) {
kfree(lzo_block->dst);
kfree(lzo_block->src);
goto out;
}
/* set the bit so we know we have to sync this register */
set_bit(reg / map->reg_stride, lzo_block->sync_bmp);
kfree(tmp_dst);
kfree(lzo_block->src);
return 0;
out:
lzo_block->dst = tmp_dst;
lzo_block->dst_len = tmp_dst_len;
return ret;
}
static int regcache_lzo_sync(struct regmap *map, unsigned int min,
unsigned int max)
{
struct regcache_lzo_ctx **lzo_blocks;
unsigned int val;
int i;
int ret;
lzo_blocks = map->cache;
i = min;
for_each_set_bit_from(i, lzo_blocks[0]->sync_bmp,
lzo_blocks[0]->sync_bmp_nbits) {
if (i > max)
continue;
ret = regcache_read(map, i, &val);
if (ret)
return ret;
/* Is this the hardware default? If so skip. */
ret = regcache_lookup_reg(map, i);
if (ret > 0 && val == map->reg_defaults[ret].def)
continue;
map->cache_bypass = true;
ret = _regmap_write(map, i, val);
map->cache_bypass = false;
if (ret)
return ret;
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
i, val);
}
return 0;
}
struct regcache_ops regcache_lzo_ops = {
.type = REGCACHE_COMPRESSED,
.name = "lzo",
.init = regcache_lzo_init,
.exit = regcache_lzo_exit,
.read = regcache_lzo_read,
.write = regcache_lzo_write,
.sync = regcache_lzo_sync
};
// SPDX-License-Identifier: GPL-2.0
//
// Register cache access API - maple tree based cache
//
// Copyright 2023 Arm, Ltd
//
// Author: Mark Brown <broonie@kernel.org>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/maple_tree.h>
#include <linux/slab.h>
#include "internal.h"
static int regcache_maple_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
struct maple_tree *mt = map->cache;
MA_STATE(mas, mt, reg, reg);
unsigned long *entry;
rcu_read_lock();
entry = mas_walk(&mas);
if (!entry) {
rcu_read_unlock();
return -ENOENT;
}
*value = entry[reg - mas.index];
rcu_read_unlock();
return 0;
}
static int regcache_maple_write(struct regmap *map, unsigned int reg,
unsigned int val)
{
struct maple_tree *mt = map->cache;
MA_STATE(mas, mt, reg, reg);
unsigned long *entry, *upper, *lower;
unsigned long index, last;
size_t lower_sz, upper_sz;
int ret;
rcu_read_lock();
entry = mas_walk(&mas);
if (entry) {
entry[reg - mas.index] = val;
rcu_read_unlock();
return 0;
}
/* Any adjacent entries to extend/merge? */
mas_set_range(&mas, reg - 1, reg + 1);
index = reg;
last = reg;
lower = mas_find(&mas, reg - 1);
if (lower) {
index = mas.index;
lower_sz = (mas.last - mas.index + 1) * sizeof(unsigned long);
}
upper = mas_find(&mas, reg + 1);
if (upper) {
last = mas.last;
upper_sz = (mas.last - mas.index + 1) * sizeof(unsigned long);
}
rcu_read_unlock();
entry = kmalloc((last - index + 1) * sizeof(unsigned long),
GFP_KERNEL);
if (!entry)
return -ENOMEM;
if (lower)
memcpy(entry, lower, lower_sz);
entry[reg - index] = val;
if (upper)
memcpy(&entry[reg - index + 1], upper, upper_sz);
/*
* This is safe because the regmap lock means the Maple lock
* is redundant, but we need to take it due to lockdep asserts
* in the maple tree code.
*/
mas_lock(&mas);
mas_set_range(&mas, index, last);
ret = mas_store_gfp(&mas, entry, GFP_KERNEL);
mas_unlock(&mas);
if (ret == 0) {
kfree(lower);
kfree(upper);
}
return ret;
}
static int regcache_maple_drop(struct regmap *map, unsigned int min,
unsigned int max)
{
struct maple_tree *mt = map->cache;
MA_STATE(mas, mt, min, max);
unsigned long *entry, *lower, *upper;
unsigned long lower_index, lower_last;
unsigned long upper_index, upper_last;
int ret;
lower = NULL;
upper = NULL;
mas_lock(&mas);
mas_for_each(&mas, entry, max) {
/*
* This is safe because the regmap lock means the
* Maple lock is redundant, but we need to take it due
* to lockdep asserts in the maple tree code.
*/
mas_unlock(&mas);
/* Do we need to save any of this entry? */
if (mas.index < min) {
lower_index = mas.index;
lower_last = min -1;
lower = kmemdup(entry, ((min - mas.index) *
sizeof(unsigned long)),
GFP_KERNEL);
if (!lower) {
ret = -ENOMEM;
goto out_unlocked;
}
}
if (mas.last > max) {
upper_index = max + 1;
upper_last = mas.last;
upper = kmemdup(&entry[max + 1],
((mas.last - max) *
sizeof(unsigned long)),
GFP_KERNEL);
if (!upper) {
ret = -ENOMEM;
goto out_unlocked;
}
}
kfree(entry);
mas_lock(&mas);
mas_erase(&mas);
/* Insert new nodes with the saved data */
if (lower) {
mas_set_range(&mas, lower_index, lower_last);
ret = mas_store_gfp(&mas, lower, GFP_KERNEL);
if (ret != 0)
goto out;
lower = NULL;
}
if (upper) {
mas_set_range(&mas, upper_index, upper_last);
ret = mas_store_gfp(&mas, upper, GFP_KERNEL);
if (ret != 0)
goto out;
upper = NULL;
}
}
out:
mas_unlock(&mas);
out_unlocked:
kfree(lower);
kfree(upper);
return ret;
}
static int regcache_maple_sync(struct regmap *map, unsigned int min,
unsigned int max)
{
struct maple_tree *mt = map->cache;
unsigned long *entry;
MA_STATE(mas, mt, min, max);
unsigned long lmin = min;
unsigned long lmax = max;
unsigned int r;
int ret;
map->cache_bypass = true;
rcu_read_lock();
mas_for_each(&mas, entry, max) {
for (r = max(mas.index, lmin); r <= min(mas.last, lmax); r++) {
ret = regcache_sync_val(map, r, entry[r - mas.index]);
if (ret != 0)
goto out;
}
}
out:
rcu_read_unlock();
map->cache_bypass = false;
return ret;
}
static int regcache_maple_exit(struct regmap *map)
{
struct maple_tree *mt = map->cache;
MA_STATE(mas, mt, 0, UINT_MAX);
unsigned int *entry;;
/* if we've already been called then just return */
if (!mt)
return 0;
mas_lock(&mas);
mas_for_each(&mas, entry, UINT_MAX)
kfree(entry);
__mt_destroy(mt);
mas_unlock(&mas);
kfree(mt);
map->cache = NULL;
return 0;
}
static int regcache_maple_init(struct regmap *map)
{
struct maple_tree *mt;
int i;
int ret;
mt = kmalloc(sizeof(*mt), GFP_KERNEL);
if (!mt)
return -ENOMEM;
map->cache = mt;
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;
}
return 0;
err:
regcache_maple_exit(map);
return ret;
}
struct regcache_ops regcache_maple_ops = {
.type = REGCACHE_MAPLE,
.name = "maple",
.init = regcache_maple_init,
.exit = regcache_maple_exit,
.read = regcache_maple_read,
.write = regcache_maple_write,
.drop = regcache_maple_drop,
.sync = regcache_maple_sync,
};
......@@ -17,9 +17,7 @@
static const struct regcache_ops *cache_types[] = {
&regcache_rbtree_ops,
#if IS_ENABLED(CONFIG_REGCACHE_COMPRESSED)
&regcache_lzo_ops,
#endif
&regcache_maple_ops,
&regcache_flat_ops,
};
......@@ -148,7 +146,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
break;
if (i == ARRAY_SIZE(cache_types)) {
dev_err(map->dev, "Could not match compress type: %d\n",
dev_err(map->dev, "Could not match cache type: %d\n",
map->cache_type);
return -EINVAL;
}
......@@ -242,7 +240,7 @@ int regcache_read(struct regmap *map,
int ret;
if (map->cache_type == REGCACHE_NONE)
return -ENOSYS;
return -EINVAL;
BUG_ON(!map->cache_ops);
......@@ -311,6 +309,8 @@ static int regcache_default_sync(struct regmap *map, unsigned int min,
continue;
ret = regcache_read(map, reg, &val);
if (ret == -ENOENT)
continue;
if (ret)
return ret;
......@@ -349,6 +349,9 @@ int regcache_sync(struct regmap *map)
const char *name;
bool bypass;
if (WARN_ON(map->cache_type == REGCACHE_NONE))
return -EINVAL;
BUG_ON(!map->cache_ops);
map->lock(map->lock_arg);
......@@ -418,6 +421,9 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
const char *name;
bool bypass;
if (WARN_ON(map->cache_type == REGCACHE_NONE))
return -EINVAL;
BUG_ON(!map->cache_ops);
map->lock(map->lock_arg);
......@@ -672,6 +678,30 @@ static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx)
return test_bit(idx, cache_present);
}
int regcache_sync_val(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;
if (!regcache_reg_needs_sync(map, reg, val))
return 0;
map->cache_bypass = true;
ret = _regmap_write(map, reg, val);
map->cache_bypass = false;
if (ret != 0) {
dev_err(map->dev, "Unable to sync register %#x. %d\n",
reg, ret);
return ret;
}
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
reg, val);
return 0;
}
static int regcache_sync_block_single(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base,
......@@ -688,21 +718,9 @@ static int regcache_sync_block_single(struct regmap *map, void *block,
continue;
val = regcache_get_val(map, block, i);
if (!regcache_reg_needs_sync(map, regtmp, val))
continue;
map->cache_bypass = true;
ret = _regmap_write(map, regtmp, val);
map->cache_bypass = false;
if (ret != 0) {
dev_err(map->dev, "Unable to sync register %#x. %d\n",
regtmp, ret);
ret = regcache_sync_val(map, regtmp, val);
if (ret != 0)
return ret;
}
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
regtmp, val);
}
return 0;
......
......@@ -329,8 +329,8 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
}
if (d->chip->set_type_config) {
ret = d->chip->set_type_config(d->config_buf, type,
irq_data, reg);
ret = d->chip->set_type_config(d->config_buf, type, irq_data,
reg, d->chip->irq_drv_data);
if (ret)
return ret;
}
......@@ -433,7 +433,10 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
* possible in order to reduce the I/O overheads.
*/
if (chip->num_main_regs) {
if (chip->no_status) {
/* no status register so default to all active */
memset32(data->status_buf, GENMASK(31, 0), chip->num_regs);
} else if (chip->num_main_regs) {
unsigned int max_main_bits;
unsigned long size;
......@@ -651,13 +654,15 @@ EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear);
* @type: The requested IRQ type.
* @irq_data: The IRQ being configured.
* @idx: Index of the irq's config registers within each array `buf[i]`
* @irq_drv_data: Driver specific IRQ data
*
* This is a &struct regmap_irq_chip->set_type_config callback suitable for
* chips with one config register. Register values are updated according to
* the &struct regmap_irq_type data associated with an IRQ.
*/
int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx)
const struct regmap_irq *irq_data,
int idx, void *irq_drv_data)
{
const struct regmap_irq_type *t = &irq_data->type;
......@@ -949,12 +954,17 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
continue;
/* Ack masked but set interrupts */
reg = d->get_irq_reg(d, d->chip->status_base, i);
ret = regmap_read(map, reg, &d->status_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n",
ret);
goto err_alloc;
if (d->chip->no_status) {
/* no status register so default to all active */
d->status_buf[i] = GENMASK(31, 0);
} else {
reg = d->get_irq_reg(d, d->chip->status_base, i);
ret = regmap_read(map, reg, &d->status_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n",
ret);
goto err_alloc;
}
}
if (chip->status_invert)
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
//
// Register map access API - Memory region
//
// 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 int regmap_ram_write(void *context, unsigned int reg, unsigned int val)
{
struct regmap_ram_data *data = context;
data->vals[reg] = val;
data->written[reg] = true;
return 0;
}
static int regmap_ram_read(void *context, unsigned int reg, unsigned int *val)
{
struct regmap_ram_data *data = context;
*val = data->vals[reg];
data->read[reg] = true;
return 0;
}
static void regmap_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_ram = {
.fast_io = true,
.reg_write = regmap_ram_write,
.reg_read = regmap_ram_read,
.free_context = regmap_ram_free_context,
};
struct regmap *__regmap_init_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->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);
map = __regmap_init(NULL, &regmap_ram, data, config,
lock_key, lock_name);
return map;
}
EXPORT_SYMBOL_GPL(__regmap_init_ram);
MODULE_LICENSE("GPL v2");
......@@ -6,44 +6,53 @@
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/soundwire/sdw.h>
#include <linux/types.h>
#include "internal.h"
static int regmap_sdw_write(void *context, unsigned int reg, unsigned int val)
static int regmap_sdw_write(void *context, const void *val_buf, size_t val_size)
{
struct device *dev = context;
struct sdw_slave *slave = dev_to_sdw_dev(dev);
/* First word of buffer contains the destination address */
u32 addr = le32_to_cpu(*(const __le32 *)val_buf);
const u8 *val = val_buf;
return sdw_write_no_pm(slave, reg, val);
return sdw_nwrite_no_pm(slave, addr, val_size - sizeof(addr), val + sizeof(addr));
}
static int regmap_sdw_read(void *context, unsigned int reg, unsigned int *val)
static int regmap_sdw_gather_write(void *context,
const void *reg_buf, size_t reg_size,
const void *val_buf, size_t val_size)
{
struct device *dev = context;
struct sdw_slave *slave = dev_to_sdw_dev(dev);
int read;
u32 addr = le32_to_cpu(*(const __le32 *)reg_buf);
read = sdw_read_no_pm(slave, reg);
if (read < 0)
return read;
return sdw_nwrite_no_pm(slave, addr, val_size, val_buf);
}
*val = read;
return 0;
static int regmap_sdw_read(void *context,
const void *reg_buf, size_t reg_size,
void *val_buf, size_t val_size)
{
struct device *dev = context;
struct sdw_slave *slave = dev_to_sdw_dev(dev);
u32 addr = le32_to_cpu(*(const __le32 *)reg_buf);
return sdw_nread_no_pm(slave, addr, val_size, val_buf);
}
static const struct regmap_bus regmap_sdw = {
.reg_read = regmap_sdw_read,
.reg_write = regmap_sdw_write,
.write = regmap_sdw_write,
.gather_write = regmap_sdw_gather_write,
.read = regmap_sdw_read,
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
};
static int regmap_sdw_config_check(const struct regmap_config *config)
{
/* All register are 8-bits wide as per MIPI Soundwire 1.0 Spec */
if (config->val_bits != 8)
return -ENOTSUPP;
/* Registers are 32 bits wide */
/* Register addresses are 32 bits wide */
if (config->reg_bits != 32)
return -ENOTSUPP;
......
......@@ -814,7 +814,7 @@ struct regmap *__regmap_init(struct device *dev,
map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
map->format.pad_bytes = config->pad_bits / 8;
map->format.reg_downshift = config->reg_downshift;
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);
......@@ -1676,6 +1676,18 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
buf[i] |= (mask >> (8 * i)) & 0xff;
}
static unsigned int regmap_reg_addr(struct regmap *map, unsigned int reg)
{
reg += map->reg_base;
if (map->format.reg_shift > 0)
reg >>= map->format.reg_shift;
else if (map->format.reg_shift < 0)
reg <<= -(map->format.reg_shift);
return reg;
}
static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool noinc)
{
......@@ -1753,8 +1765,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
return ret;
}
reg += map->reg_base;
reg >>= map->format.reg_downshift;
reg = regmap_reg_addr(map, reg);
map->format.format_reg(map->work_buf, reg, map->reg_shift);
regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
map->write_flag_mask);
......@@ -1924,8 +1935,7 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg,
return ret;
}
reg += map->reg_base;
reg >>= map->format.reg_downshift;
reg = regmap_reg_addr(map, reg);
map->format.format_write(map, reg, val);
trace_regmap_hw_write_start(map, reg, 1);
......@@ -1941,9 +1951,17 @@ static int _regmap_bus_reg_write(void *context, unsigned int reg,
unsigned int val)
{
struct regmap *map = context;
struct regmap_range_node *range;
int ret;
reg += map->reg_base;
reg >>= map->format.reg_downshift;
range = _regmap_range_lookup(map, reg);
if (range) {
ret = _regmap_select_page(map, &reg, range, 1);
if (ret != 0)
return ret;
}
reg = regmap_reg_addr(map, reg);
return map->bus->reg_write(map->bus_context, reg, val);
}
......@@ -2494,8 +2512,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map,
unsigned int reg = regs[i].reg;
unsigned int val = regs[i].def;
trace_regmap_hw_write_start(map, reg, 1);
reg += map->reg_base;
reg >>= map->format.reg_downshift;
reg = regmap_reg_addr(map, reg);
map->format.format_reg(u8, reg, map->reg_shift);
u8 += reg_bytes + pad_bytes;
map->format.format_val(u8, val, 0);
......@@ -2821,8 +2838,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
return ret;
}
reg += map->reg_base;
reg >>= map->format.reg_downshift;
reg = regmap_reg_addr(map, reg);
map->format.format_reg(map->work_buf, reg, map->reg_shift);
regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
map->read_flag_mask);
......@@ -2841,9 +2857,17 @@ static int _regmap_bus_reg_read(void *context, unsigned int reg,
unsigned int *val)
{
struct regmap *map = context;
struct regmap_range_node *range;
int ret;
reg += map->reg_base;
reg >>= map->format.reg_downshift;
range = _regmap_range_lookup(map, reg);
if (range) {
ret = _regmap_select_page(map, &reg, range, 1);
if (ret != 0)
return ret;
}
reg = regmap_reg_addr(map, reg);
return map->bus->reg_read(map->bus_context, reg, val);
}
......@@ -3235,8 +3259,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
*change = false;
if (regmap_volatile(map, reg) && map->reg_update_bits) {
reg += map->reg_base;
reg >>= map->format.reg_downshift;
reg = regmap_reg_addr(map, reg);
ret = map->reg_update_bits(map->bus_context, reg, mask, val);
if (ret == 0 && change)
*change = true;
......
......@@ -125,7 +125,7 @@ static int ocelot_spi_initialize(struct device *dev)
static const struct regmap_config ocelot_spi_regmap_config = {
.reg_bits = 24,
.reg_stride = 4,
.reg_downshift = 2,
.reg_shift = REGMAP_DOWNSHIFT(2),
.val_bits = 32,
.write_flag_mask = 0x80,
......
......@@ -46,12 +46,20 @@ struct sdw_slave;
#define REGMAP_MDIO_C45_DEVAD_MASK GENMASK(20, 16)
#define REGMAP_MDIO_C45_REGNUM_MASK GENMASK(15, 0)
/*
* regmap.reg_shift indicates by how much we must shift registers prior to
* performing any operation. It's a signed value, positive numbers means
* downshifting the register's address, while negative numbers means upshifting.
*/
#define REGMAP_UPSHIFT(s) (-(s))
#define REGMAP_DOWNSHIFT(s) (s)
/* An enum of all the supported cache types */
enum regcache_type {
REGCACHE_NONE,
REGCACHE_RBTREE,
REGCACHE_COMPRESSED,
REGCACHE_FLAT,
REGCACHE_MAPLE,
};
/**
......@@ -246,8 +254,9 @@ typedef void (*regmap_unlock)(void *);
* @reg_stride: The register address stride. Valid register addresses are a
* multiple of this value. If set to 0, a value of 1 will be
* used.
* @reg_downshift: The number of bits to downshift the register before
* performing any operations.
* @reg_shift: The number of bits to shift the register before performing any
* operations. Any positive number will be downshifted, and negative
* values will be upshifted
* @reg_base: Value to be added to every register address before performing any
* operation.
* @pad_bits: Number of bits of padding between register and value.
......@@ -381,7 +390,7 @@ struct regmap_config {
int reg_bits;
int reg_stride;
int reg_downshift;
int reg_shift;
unsigned int reg_base;
int pad_bits;
int val_bits;
......@@ -1551,6 +1560,7 @@ struct regmap_irq_chip_data;
* @use_ack: Use @ack register even if it is zero.
* @ack_invert: Inverted ack register: cleared bits for ack.
* @clear_ack: Use this to set 1 and 0 or vice-versa to clear interrupts.
* @status_invert: Inverted status register: cleared bits are active interrupts.
* @wake_invert: Inverted wake register: cleared bits are wake enabled.
* @type_in_mask: Use the mask registers for controlling irq type. Use this if
* the hardware provides separate bits for rising/falling edge
......@@ -1560,18 +1570,20 @@ struct regmap_irq_chip_data;
* @clear_on_unmask: For chips with interrupts cleared on read: read the status
* registers before unmasking interrupts to clear any bits
* set when they were masked.
* @runtime_pm: Hold a runtime PM lock on the device when accessing it.
* @not_fixed_stride: Used when chip peripherals are not laid out with fixed
* stride. Must be used with sub_reg_offsets containing the
* offsets to each peripheral. Deprecated; the same thing
* can be accomplished with a @get_irq_reg callback, without
* the need for a @sub_reg_offsets table.
* @status_invert: Inverted status register: cleared bits are active interrupts.
* @runtime_pm: Hold a runtime PM lock on the device when accessing it.
* @no_status: No status register: all interrupts assumed generated by device.
*
* @num_regs: Number of registers in each control bank.
*
* @irqs: Descriptors for individual IRQs. Interrupt numbers are
* assigned based on the index in the array of the interrupt.
* @num_irqs: Number of descriptors.
*
* @num_type_reg: Number of type registers. Deprecated, use config registers
* instead.
* @num_virt_regs: Number of non-standard irq configuration registers.
......@@ -1579,6 +1591,7 @@ struct regmap_irq_chip_data;
* instead.
* @num_config_bases: Number of config base registers.
* @num_config_regs: Number of config registers for each config base register.
*
* @handle_pre_irq: Driver specific callback to handle interrupt from device
* before regmap_irq_handler process the interrupts.
* @handle_post_irq: Driver specific callback to handle interrupt from device
......@@ -1625,12 +1638,13 @@ struct regmap_irq_chip {
unsigned int use_ack:1;
unsigned int ack_invert:1;
unsigned int clear_ack:1;
unsigned int status_invert:1;
unsigned int wake_invert:1;
unsigned int runtime_pm:1;
unsigned int type_in_mask:1;
unsigned int clear_on_unmask:1;
unsigned int runtime_pm:1;
unsigned int not_fixed_stride:1;
unsigned int status_invert:1;
unsigned int no_status:1;
int num_regs;
......@@ -1650,7 +1664,8 @@ struct regmap_irq_chip {
int (*set_type_virt)(unsigned int **buf, unsigned int type,
unsigned long hwirq, int reg);
int (*set_type_config)(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx);
const struct regmap_irq *irq_data, int idx,
void *irq_drv_data);
unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data,
unsigned int base, int index);
void *irq_drv_data;
......@@ -1659,7 +1674,8 @@ struct regmap_irq_chip {
unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data,
unsigned int base, int index);
int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx);
const struct regmap_irq *irq_data,
int idx, void *irq_drv_data);
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
int irq_base, const struct regmap_irq_chip *chip,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment