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 ...@@ -9,10 +9,12 @@ config REGMAP
select MDIO_BUS if REGMAP_MDIO select MDIO_BUS if REGMAP_MDIO
bool bool
config REGCACHE_COMPRESSED config REGMAP_KUNIT
select LZO_COMPRESS tristate "KUnit tests for regmap"
select LZO_DECOMPRESS depends on KUNIT
bool default KUNIT_ALL_TESTS
select REGMAP
select REGMAP_RAM
config REGMAP_AC97 config REGMAP_AC97
tristate tristate
...@@ -46,6 +48,9 @@ config REGMAP_MMIO ...@@ -46,6 +48,9 @@ config REGMAP_MMIO
config REGMAP_IRQ config REGMAP_IRQ
bool bool
config REGMAP_RAM
tristate
config REGMAP_SOUNDWIRE config REGMAP_SOUNDWIRE
tristate tristate
depends on SOUNDWIRE depends on SOUNDWIRE
......
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
CFLAGS_regmap.o := -I$(src) CFLAGS_regmap.o := -I$(src)
obj-$(CONFIG_REGMAP) += regmap.o regcache.o obj-$(CONFIG_REGMAP) += regmap.o regcache.o
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o regcache-maple.o
obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.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_AC97) += regmap-ac97.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.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_SLIMBUS) += regmap-slimbus.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
......
...@@ -31,8 +31,8 @@ struct regmap_format { ...@@ -31,8 +31,8 @@ struct regmap_format {
size_t buf_size; size_t buf_size;
size_t reg_bytes; size_t reg_bytes;
size_t pad_bytes; size_t pad_bytes;
size_t reg_downshift;
size_t val_bytes; size_t val_bytes;
s8 reg_shift;
void (*format_write)(struct regmap *map, void (*format_write)(struct regmap *map,
unsigned int reg, unsigned int val); unsigned int reg, unsigned int val);
void (*format_reg)(void *buf, unsigned int reg, unsigned int shift); 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, ...@@ -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, bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
unsigned int val); unsigned int val);
int regcache_lookup_reg(struct regmap *map, unsigned int reg); 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, int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool noinc); const void *val, size_t val_len, bool noinc);
...@@ -281,7 +282,7 @@ enum regmap_endian regmap_get_val_endian(struct device *dev, ...@@ -281,7 +282,7 @@ enum regmap_endian regmap_get_val_endian(struct device *dev,
const struct regmap_config *config); const struct regmap_config *config);
extern struct regcache_ops regcache_rbtree_ops; 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; extern struct regcache_ops regcache_flat_ops;
static inline const char *regmap_name(const struct regmap *map) 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, ...@@ -307,4 +308,23 @@ static inline unsigned int regcache_get_index_by_order(const struct regmap *map,
return reg >> map->reg_stride_order; 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 #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 @@ ...@@ -17,9 +17,7 @@
static const struct regcache_ops *cache_types[] = { static const struct regcache_ops *cache_types[] = {
&regcache_rbtree_ops, &regcache_rbtree_ops,
#if IS_ENABLED(CONFIG_REGCACHE_COMPRESSED) &regcache_maple_ops,
&regcache_lzo_ops,
#endif
&regcache_flat_ops, &regcache_flat_ops,
}; };
...@@ -148,7 +146,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) ...@@ -148,7 +146,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
break; break;
if (i == ARRAY_SIZE(cache_types)) { 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); map->cache_type);
return -EINVAL; return -EINVAL;
} }
...@@ -242,7 +240,7 @@ int regcache_read(struct regmap *map, ...@@ -242,7 +240,7 @@ int regcache_read(struct regmap *map,
int ret; int ret;
if (map->cache_type == REGCACHE_NONE) if (map->cache_type == REGCACHE_NONE)
return -ENOSYS; return -EINVAL;
BUG_ON(!map->cache_ops); BUG_ON(!map->cache_ops);
...@@ -311,6 +309,8 @@ static int regcache_default_sync(struct regmap *map, unsigned int min, ...@@ -311,6 +309,8 @@ static int regcache_default_sync(struct regmap *map, unsigned int min,
continue; continue;
ret = regcache_read(map, reg, &val); ret = regcache_read(map, reg, &val);
if (ret == -ENOENT)
continue;
if (ret) if (ret)
return ret; return ret;
...@@ -349,6 +349,9 @@ int regcache_sync(struct regmap *map) ...@@ -349,6 +349,9 @@ int regcache_sync(struct regmap *map)
const char *name; const char *name;
bool bypass; bool bypass;
if (WARN_ON(map->cache_type == REGCACHE_NONE))
return -EINVAL;
BUG_ON(!map->cache_ops); BUG_ON(!map->cache_ops);
map->lock(map->lock_arg); map->lock(map->lock_arg);
...@@ -418,6 +421,9 @@ int regcache_sync_region(struct regmap *map, unsigned int min, ...@@ -418,6 +421,9 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
const char *name; const char *name;
bool bypass; bool bypass;
if (WARN_ON(map->cache_type == REGCACHE_NONE))
return -EINVAL;
BUG_ON(!map->cache_ops); BUG_ON(!map->cache_ops);
map->lock(map->lock_arg); map->lock(map->lock_arg);
...@@ -672,6 +678,30 @@ static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx) ...@@ -672,6 +678,30 @@ static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx)
return test_bit(idx, cache_present); 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, static int regcache_sync_block_single(struct regmap *map, void *block,
unsigned long *cache_present, unsigned long *cache_present,
unsigned int block_base, unsigned int block_base,
...@@ -688,22 +718,10 @@ static int regcache_sync_block_single(struct regmap *map, void *block, ...@@ -688,22 +718,10 @@ static int regcache_sync_block_single(struct regmap *map, void *block,
continue; continue;
val = regcache_get_val(map, block, i); val = regcache_get_val(map, block, i);
if (!regcache_reg_needs_sync(map, regtmp, val)) ret = regcache_sync_val(map, regtmp, val);
continue; if (ret != 0)
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);
return ret; return ret;
} }
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
regtmp, val);
}
return 0; return 0;
} }
......
...@@ -329,8 +329,8 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type) ...@@ -329,8 +329,8 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
} }
if (d->chip->set_type_config) { if (d->chip->set_type_config) {
ret = d->chip->set_type_config(d->config_buf, type, ret = d->chip->set_type_config(d->config_buf, type, irq_data,
irq_data, reg); reg, d->chip->irq_drv_data);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -433,7 +433,10 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) ...@@ -433,7 +433,10 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
* possible in order to reduce the I/O overheads. * 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 int max_main_bits;
unsigned long size; unsigned long size;
...@@ -651,13 +654,15 @@ EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear); ...@@ -651,13 +654,15 @@ EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear);
* @type: The requested IRQ type. * @type: The requested IRQ type.
* @irq_data: The IRQ being configured. * @irq_data: The IRQ being configured.
* @idx: Index of the irq's config registers within each array `buf[i]` * @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 * This is a &struct regmap_irq_chip->set_type_config callback suitable for
* chips with one config register. Register values are updated according to * chips with one config register. Register values are updated according to
* the &struct regmap_irq_type data associated with an IRQ. * the &struct regmap_irq_type data associated with an IRQ.
*/ */
int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type, 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; const struct regmap_irq_type *t = &irq_data->type;
...@@ -949,6 +954,10 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -949,6 +954,10 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
continue; continue;
/* Ack masked but set interrupts */ /* Ack masked but set interrupts */
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); reg = d->get_irq_reg(d, d->chip->status_base, i);
ret = regmap_read(map, reg, &d->status_buf[i]); ret = regmap_read(map, reg, &d->status_buf[i]);
if (ret != 0) { if (ret != 0) {
...@@ -956,6 +965,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -956,6 +965,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
ret); ret);
goto err_alloc; goto err_alloc;
} }
}
if (chip->status_invert) if (chip->status_invert)
d->status_buf[i] = ~d->status_buf[i]; d->status_buf[i] = ~d->status_buf[i];
......
// SPDX-License-Identifier: GPL-2.0
//
// regmap KUnit tests
//
// Copyright 2023 Arm Ltd
#include <kunit/test.h>
#include "internal.h"
#define BLOCK_TEST_SIZE 12
static const struct regmap_config test_regmap_config = {
.max_register = BLOCK_TEST_SIZE,
.reg_stride = 1,
.val_bits = sizeof(unsigned int) * 8,
};
struct regcache_types {
enum regcache_type type;
const char *name;
};
static void case_to_desc(const struct regcache_types *t, char *desc)
{
strcpy(desc, t->name);
}
static const struct regcache_types regcache_types_list[] = {
{ REGCACHE_NONE, "none" },
{ REGCACHE_FLAT, "flat" },
{ REGCACHE_RBTREE, "rbtree" },
{ REGCACHE_MAPLE, "maple" },
};
KUNIT_ARRAY_PARAM(regcache_types, regcache_types_list, case_to_desc);
static const struct regcache_types real_cache_types_list[] = {
{ REGCACHE_FLAT, "flat" },
{ REGCACHE_RBTREE, "rbtree" },
{ REGCACHE_MAPLE, "maple" },
};
KUNIT_ARRAY_PARAM(real_cache_types, real_cache_types_list, case_to_desc);
static const struct regcache_types sparse_cache_types_list[] = {
{ REGCACHE_RBTREE, "rbtree" },
{ REGCACHE_MAPLE, "maple" },
};
KUNIT_ARRAY_PARAM(sparse_cache_types, sparse_cache_types_list, case_to_desc);
static struct regmap *gen_regmap(struct regmap_config *config,
struct regmap_ram_data **data)
{
unsigned int *buf;
struct regmap *ret;
size_t size = (config->max_register + 1) * sizeof(unsigned int);
int i;
struct reg_default *defaults;
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 = buf;
if (config->num_reg_defaults) {
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 * config->reg_stride;
defaults[i].def = buf[i * config->reg_stride];
}
}
ret = regmap_init_ram(config, *data);
if (IS_ERR(ret)) {
kfree(buf);
kfree(*data);
}
return ret;
}
static void basic_read_write(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, rval;
config = test_regmap_config;
config.cache_type = t->type;
map = gen_regmap(&config, &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);
/* If using a cache the cache satisfied the read */
KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[0]);
regmap_exit(map);
}
static void bulk_write(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[BLOCK_TEST_SIZE], rval[BLOCK_TEST_SIZE];
int i;
config = test_regmap_config;
config.cache_type = t->type;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
get_random_bytes(&val, sizeof(val));
/*
* Data written via the bulk API can be read back with single
* reads.
*/
KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, 0, val,
BLOCK_TEST_SIZE));
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval[i]));
KUNIT_EXPECT_MEMEQ(test, val, rval, sizeof(val));
/* If using a cache the cache satisfied the read */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[i]);
regmap_exit(map);
}
static void bulk_read(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[BLOCK_TEST_SIZE], rval[BLOCK_TEST_SIZE];
int i;
config = test_regmap_config;
config.cache_type = t->type;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
get_random_bytes(&val, sizeof(val));
/* Data written as single writes can be read via the bulk API */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, val[i]));
KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval,
BLOCK_TEST_SIZE));
KUNIT_EXPECT_MEMEQ(test, val, rval, sizeof(val));
/* If using a cache the cache satisfied the read */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[i]);
regmap_exit(map);
}
static void reg_defaults(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 rval[BLOCK_TEST_SIZE];
int i;
config = test_regmap_config;
config.cache_type = t->type;
config.num_reg_defaults = BLOCK_TEST_SIZE;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
/* Read back the expected default data */
KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval,
BLOCK_TEST_SIZE));
KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval));
/* The data should have been read from cache if there was one */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[i]);
}
static void reg_defaults_read_dev(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 rval[BLOCK_TEST_SIZE];
int i;
config = test_regmap_config;
config.cache_type = t->type;
config.num_reg_defaults_raw = BLOCK_TEST_SIZE;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
/* We should have read the cache defaults back from the map */
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
KUNIT_EXPECT_EQ(test, t->type != REGCACHE_NONE, data->read[i]);
data->read[i] = false;
}
/* Read back the expected default data */
KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval,
BLOCK_TEST_SIZE));
KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval));
/* The data should have been read from cache if there was one */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[i]);
}
static void register_patch(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;
struct reg_sequence patch[2];
unsigned int rval[BLOCK_TEST_SIZE];
int i;
/* We need defaults so readback works */
config = test_regmap_config;
config.cache_type = t->type;
config.num_reg_defaults = BLOCK_TEST_SIZE;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
/* Stash the original values */
KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval,
BLOCK_TEST_SIZE));
/* Patch a couple of values */
patch[0].reg = 2;
patch[0].def = rval[2] + 1;
patch[0].delay_us = 0;
patch[1].reg = 5;
patch[1].def = rval[5] + 1;
patch[1].delay_us = 0;
KUNIT_EXPECT_EQ(test, 0, regmap_register_patch(map, patch,
ARRAY_SIZE(patch)));
/* Only the patched registers are written */
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
switch (i) {
case 2:
case 5:
KUNIT_EXPECT_TRUE(test, data->written[i]);
KUNIT_EXPECT_EQ(test, data->vals[i], rval[i] + 1);
break;
default:
KUNIT_EXPECT_FALSE(test, data->written[i]);
KUNIT_EXPECT_EQ(test, data->vals[i], rval[i]);
break;
}
}
regmap_exit(map);
}
static void stride(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 rval;
int i;
config = test_regmap_config;
config.cache_type = t->type;
config.reg_stride = 2;
config.num_reg_defaults = BLOCK_TEST_SIZE / 2;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
/* Only even registers can be accessed, try both read and write */
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
data->read[i] = false;
data->written[i] = false;
if (i % 2) {
KUNIT_EXPECT_NE(test, 0, regmap_read(map, i, &rval));
KUNIT_EXPECT_NE(test, 0, regmap_write(map, i, rval));
KUNIT_EXPECT_FALSE(test, data->read[i]);
KUNIT_EXPECT_FALSE(test, data->written[i]);
} else {
KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval));
KUNIT_EXPECT_EQ(test, data->vals[i], rval);
KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE,
data->read[i]);
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, rval));
KUNIT_EXPECT_TRUE(test, data->written[i]);
}
}
regmap_exit(map);
}
static struct regmap_range_cfg test_range = {
.selector_reg = 1,
.selector_mask = 0xff,
.window_start = 4,
.window_len = 10,
.range_min = 20,
.range_max = 40,
};
static bool test_range_volatile(struct device *dev, unsigned int reg)
{
if (reg >= test_range.window_start &&
reg <= test_range.selector_reg + test_range.window_len)
return true;
if (reg >= test_range.range_min && reg <= test_range.range_max)
return true;
return false;
}
static void basic_ranges(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.volatile_reg = test_range_volatile;
config.ranges = &test_range;
config.num_ranges = 1;
config.max_register = test_range.range_max;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
for (i = test_range.range_min; i < test_range.range_max; i++) {
data->read[i] = false;
data->written[i] = false;
}
/* Reset the page to a non-zero value to trigger a change */
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, test_range.selector_reg,
test_range.range_max));
/* Check we set the page and use the window for writes */
data->written[test_range.selector_reg] = false;
data->written[test_range.window_start] = false;
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, test_range.range_min, 0));
KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
KUNIT_EXPECT_TRUE(test, data->written[test_range.window_start]);
data->written[test_range.selector_reg] = false;
data->written[test_range.window_start] = false;
KUNIT_EXPECT_EQ(test, 0, regmap_write(map,
test_range.range_min +
test_range.window_len,
0));
KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
KUNIT_EXPECT_TRUE(test, data->written[test_range.window_start]);
/* Same for reads */
data->written[test_range.selector_reg] = false;
data->read[test_range.window_start] = false;
KUNIT_EXPECT_EQ(test, 0, regmap_read(map, test_range.range_min, &val));
KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
KUNIT_EXPECT_TRUE(test, data->read[test_range.window_start]);
data->written[test_range.selector_reg] = false;
data->read[test_range.window_start] = false;
KUNIT_EXPECT_EQ(test, 0, regmap_read(map,
test_range.range_min +
test_range.window_len,
&val));
KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
KUNIT_EXPECT_TRUE(test, data->read[test_range.window_start]);
/* No physical access triggered in the virtual range */
for (i = test_range.range_min; i < test_range.range_max; i++) {
KUNIT_EXPECT_FALSE(test, data->read[i]);
KUNIT_EXPECT_FALSE(test, data->written[i]);
}
regmap_exit(map);
}
/* Try to stress dynamic creation of cache data structures */
static void stress_insert(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 rval, *vals;
size_t buf_sz;
int i;
config = test_regmap_config;
config.cache_type = t->type;
config.max_register = 300;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
vals = kunit_kcalloc(test, sizeof(unsigned long), config.max_register,
GFP_KERNEL);
KUNIT_ASSERT_FALSE(test, vals == NULL);
buf_sz = sizeof(unsigned long) * config.max_register;
get_random_bytes(vals, buf_sz);
/* Write data into the map/cache in ever decreasing strides */
for (i = 0; i < config.max_register; i += 100)
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
for (i = 0; i < config.max_register; i += 50)
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
for (i = 0; i < config.max_register; i += 25)
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
for (i = 0; i < config.max_register; i += 10)
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
for (i = 0; i < config.max_register; i += 5)
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
for (i = 0; i < config.max_register; i += 3)
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
for (i = 0; i < config.max_register; i += 2)
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
for (i = 0; i < config.max_register; i++)
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
/* Do reads from the cache (if there is one) match? */
for (i = 0; i < config.max_register; i ++) {
KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval));
KUNIT_EXPECT_EQ(test, rval, vals[i]);
KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[i]);
}
regmap_exit(map);
}
static void cache_bypass(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, rval;
config = test_regmap_config;
config.cache_type = t->type;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
get_random_bytes(&val, sizeof(val));
/* Ensure the cache has a value in it */
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val));
/* Bypass then write a different value */
regcache_cache_bypass(map, true);
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val + 1));
/* Read the bypassed value */
KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval));
KUNIT_EXPECT_EQ(test, val + 1, rval);
KUNIT_EXPECT_EQ(test, data->vals[0], rval);
/* Disable bypass, the cache should still return the original value */
regcache_cache_bypass(map, false);
KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval));
KUNIT_EXPECT_EQ(test, val, rval);
regmap_exit(map);
}
static void cache_sync(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[BLOCK_TEST_SIZE];
int i;
config = test_regmap_config;
config.cache_type = t->type;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
get_random_bytes(&val, sizeof(val));
/* Put some data into the cache */
KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, 0, val,
BLOCK_TEST_SIZE));
for (i = 0; i < BLOCK_TEST_SIZE; i++)
data->written[i] = false;
/* Trash the data on the device itself then resync */
regcache_mark_dirty(map);
memset(data->vals, 0, sizeof(val));
KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
/* Did we just write the correct data out? */
KUNIT_EXPECT_MEMEQ(test, data->vals, val, sizeof(val));
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, true, data->written[i]);
regmap_exit(map);
}
static void cache_sync_defaults(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;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
get_random_bytes(&val, sizeof(val));
/* Change the value of one register */
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 2, val));
/* Resync */
regcache_mark_dirty(map);
for (i = 0; i < BLOCK_TEST_SIZE; i++)
data->written[i] = false;
KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
/* Did we just sync the one register we touched? */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, i == 2, data->written[i]);
regmap_exit(map);
}
static void cache_sync_patch(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;
struct reg_sequence patch[2];
unsigned int rval[BLOCK_TEST_SIZE], val;
int i;
/* We need defaults so readback works */
config = test_regmap_config;
config.cache_type = t->type;
config.num_reg_defaults = BLOCK_TEST_SIZE;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
/* Stash the original values */
KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval,
BLOCK_TEST_SIZE));
/* Patch a couple of values */
patch[0].reg = 2;
patch[0].def = rval[2] + 1;
patch[0].delay_us = 0;
patch[1].reg = 5;
patch[1].def = rval[5] + 1;
patch[1].delay_us = 0;
KUNIT_EXPECT_EQ(test, 0, regmap_register_patch(map, patch,
ARRAY_SIZE(patch)));
/* Sync the cache */
regcache_mark_dirty(map);
for (i = 0; i < BLOCK_TEST_SIZE; i++)
data->written[i] = false;
KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
/* The patch should be on the device but not in the cache */
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &val));
KUNIT_EXPECT_EQ(test, val, rval[i]);
switch (i) {
case 2:
case 5:
KUNIT_EXPECT_EQ(test, true, data->written[i]);
KUNIT_EXPECT_EQ(test, data->vals[i], rval[i] + 1);
break;
default:
KUNIT_EXPECT_EQ(test, false, data->written[i]);
KUNIT_EXPECT_EQ(test, data->vals[i], rval[i]);
break;
}
}
regmap_exit(map);
}
static void cache_drop(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 rval[BLOCK_TEST_SIZE];
int i;
config = test_regmap_config;
config.cache_type = t->type;
config.num_reg_defaults = BLOCK_TEST_SIZE;
map = gen_regmap(&config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
/* Ensure the data is read from the cache */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
data->read[i] = false;
KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval,
BLOCK_TEST_SIZE));
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
KUNIT_EXPECT_FALSE(test, data->read[i]);
data->read[i] = false;
}
KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval));
/* Drop some registers */
KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 3, 5));
/* Reread and check only the dropped registers hit the device. */
KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval,
BLOCK_TEST_SIZE));
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, data->read[i], i >= 3 && i <= 5);
KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval));
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(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),
KUNIT_CASE_PARAM(stride, regcache_types_gen_params),
KUNIT_CASE_PARAM(basic_ranges, regcache_types_gen_params),
KUNIT_CASE_PARAM(stress_insert, regcache_types_gen_params),
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_patch, real_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_drop, sparse_cache_types_gen_params),
{}
};
static struct kunit_suite regmap_test_suite = {
.name = "regmap",
.test_cases = regmap_test_cases,
};
kunit_test_suite(regmap_test_suite);
MODULE_LICENSE("GPL v2");
// 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 @@ ...@@ -6,44 +6,53 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw.h>
#include <linux/types.h>
#include "internal.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 device *dev = context;
struct sdw_slave *slave = dev_to_sdw_dev(dev); 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 device *dev = context;
struct sdw_slave *slave = dev_to_sdw_dev(dev); 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); return sdw_nwrite_no_pm(slave, addr, val_size, val_buf);
if (read < 0) }
return read;
*val = read; static int regmap_sdw_read(void *context,
return 0; 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 = { static const struct regmap_bus regmap_sdw = {
.reg_read = regmap_sdw_read, .write = regmap_sdw_write,
.reg_write = regmap_sdw_write, .gather_write = regmap_sdw_gather_write,
.read = regmap_sdw_read,
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE, .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
.val_format_endian_default = REGMAP_ENDIAN_LITTLE, .val_format_endian_default = REGMAP_ENDIAN_LITTLE,
}; };
static int regmap_sdw_config_check(const struct regmap_config *config) static int regmap_sdw_config_check(const struct regmap_config *config)
{ {
/* All register are 8-bits wide as per MIPI Soundwire 1.0 Spec */ /* Register addresses are 32 bits wide */
if (config->val_bits != 8)
return -ENOTSUPP;
/* Registers are 32 bits wide */
if (config->reg_bits != 32) if (config->reg_bits != 32)
return -ENOTSUPP; return -ENOTSUPP;
......
...@@ -814,7 +814,7 @@ struct regmap *__regmap_init(struct device *dev, ...@@ -814,7 +814,7 @@ struct regmap *__regmap_init(struct device *dev,
map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
map->format.pad_bytes = config->pad_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.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
map->format.buf_size = DIV_ROUND_UP(config->reg_bits + map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
config->val_bits + config->pad_bits, 8); 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, ...@@ -1676,6 +1676,18 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
buf[i] |= (mask >> (8 * i)) & 0xff; 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, static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool noinc) 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, ...@@ -1753,8 +1765,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
return ret; return ret;
} }
reg += map->reg_base; reg = regmap_reg_addr(map, reg);
reg >>= map->format.reg_downshift;
map->format.format_reg(map->work_buf, reg, map->reg_shift); map->format.format_reg(map->work_buf, reg, map->reg_shift);
regmap_set_work_buf_flag_mask(map, map->format.reg_bytes, regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
map->write_flag_mask); map->write_flag_mask);
...@@ -1924,8 +1935,7 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, ...@@ -1924,8 +1935,7 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg,
return ret; return ret;
} }
reg += map->reg_base; reg = regmap_reg_addr(map, reg);
reg >>= map->format.reg_downshift;
map->format.format_write(map, reg, val); map->format.format_write(map, reg, val);
trace_regmap_hw_write_start(map, reg, 1); trace_regmap_hw_write_start(map, reg, 1);
...@@ -1941,9 +1951,17 @@ static int _regmap_bus_reg_write(void *context, unsigned int reg, ...@@ -1941,9 +1951,17 @@ static int _regmap_bus_reg_write(void *context, unsigned int reg,
unsigned int val) unsigned int val)
{ {
struct regmap *map = context; struct regmap *map = context;
struct regmap_range_node *range;
int ret;
reg += map->reg_base; range = _regmap_range_lookup(map, reg);
reg >>= map->format.reg_downshift; 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); return map->bus->reg_write(map->bus_context, reg, val);
} }
...@@ -2494,8 +2512,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map, ...@@ -2494,8 +2512,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map,
unsigned int reg = regs[i].reg; unsigned int reg = regs[i].reg;
unsigned int val = regs[i].def; unsigned int val = regs[i].def;
trace_regmap_hw_write_start(map, reg, 1); trace_regmap_hw_write_start(map, reg, 1);
reg += map->reg_base; reg = regmap_reg_addr(map, reg);
reg >>= map->format.reg_downshift;
map->format.format_reg(u8, reg, map->reg_shift); map->format.format_reg(u8, reg, map->reg_shift);
u8 += reg_bytes + pad_bytes; u8 += reg_bytes + pad_bytes;
map->format.format_val(u8, val, 0); map->format.format_val(u8, val, 0);
...@@ -2821,8 +2838,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, ...@@ -2821,8 +2838,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
return ret; return ret;
} }
reg += map->reg_base; reg = regmap_reg_addr(map, reg);
reg >>= map->format.reg_downshift;
map->format.format_reg(map->work_buf, reg, map->reg_shift); map->format.format_reg(map->work_buf, reg, map->reg_shift);
regmap_set_work_buf_flag_mask(map, map->format.reg_bytes, regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
map->read_flag_mask); map->read_flag_mask);
...@@ -2841,9 +2857,17 @@ static int _regmap_bus_reg_read(void *context, unsigned int reg, ...@@ -2841,9 +2857,17 @@ static int _regmap_bus_reg_read(void *context, unsigned int reg,
unsigned int *val) unsigned int *val)
{ {
struct regmap *map = context; struct regmap *map = context;
struct regmap_range_node *range;
int ret;
reg += map->reg_base; range = _regmap_range_lookup(map, reg);
reg >>= map->format.reg_downshift; 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); 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, ...@@ -3235,8 +3259,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
*change = false; *change = false;
if (regmap_volatile(map, reg) && map->reg_update_bits) { if (regmap_volatile(map, reg) && map->reg_update_bits) {
reg += map->reg_base; reg = regmap_reg_addr(map, reg);
reg >>= map->format.reg_downshift;
ret = map->reg_update_bits(map->bus_context, reg, mask, val); ret = map->reg_update_bits(map->bus_context, reg, mask, val);
if (ret == 0 && change) if (ret == 0 && change)
*change = true; *change = true;
......
...@@ -125,7 +125,7 @@ static int ocelot_spi_initialize(struct device *dev) ...@@ -125,7 +125,7 @@ static int ocelot_spi_initialize(struct device *dev)
static const struct regmap_config ocelot_spi_regmap_config = { static const struct regmap_config ocelot_spi_regmap_config = {
.reg_bits = 24, .reg_bits = 24,
.reg_stride = 4, .reg_stride = 4,
.reg_downshift = 2, .reg_shift = REGMAP_DOWNSHIFT(2),
.val_bits = 32, .val_bits = 32,
.write_flag_mask = 0x80, .write_flag_mask = 0x80,
......
...@@ -46,12 +46,20 @@ struct sdw_slave; ...@@ -46,12 +46,20 @@ struct sdw_slave;
#define REGMAP_MDIO_C45_DEVAD_MASK GENMASK(20, 16) #define REGMAP_MDIO_C45_DEVAD_MASK GENMASK(20, 16)
#define REGMAP_MDIO_C45_REGNUM_MASK GENMASK(15, 0) #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 */ /* An enum of all the supported cache types */
enum regcache_type { enum regcache_type {
REGCACHE_NONE, REGCACHE_NONE,
REGCACHE_RBTREE, REGCACHE_RBTREE,
REGCACHE_COMPRESSED,
REGCACHE_FLAT, REGCACHE_FLAT,
REGCACHE_MAPLE,
}; };
/** /**
...@@ -246,8 +254,9 @@ typedef void (*regmap_unlock)(void *); ...@@ -246,8 +254,9 @@ typedef void (*regmap_unlock)(void *);
* @reg_stride: The register address stride. Valid register addresses are a * @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 * multiple of this value. If set to 0, a value of 1 will be
* used. * used.
* @reg_downshift: The number of bits to downshift the register before * @reg_shift: The number of bits to shift the register before performing any
* performing any operations. * 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 * @reg_base: Value to be added to every register address before performing any
* operation. * operation.
* @pad_bits: Number of bits of padding between register and value. * @pad_bits: Number of bits of padding between register and value.
...@@ -381,7 +390,7 @@ struct regmap_config { ...@@ -381,7 +390,7 @@ struct regmap_config {
int reg_bits; int reg_bits;
int reg_stride; int reg_stride;
int reg_downshift; int reg_shift;
unsigned int reg_base; unsigned int reg_base;
int pad_bits; int pad_bits;
int val_bits; int val_bits;
...@@ -1551,6 +1560,7 @@ struct regmap_irq_chip_data; ...@@ -1551,6 +1560,7 @@ struct regmap_irq_chip_data;
* @use_ack: Use @ack register even if it is zero. * @use_ack: Use @ack register even if it is zero.
* @ack_invert: Inverted ack register: cleared bits for ack. * @ack_invert: Inverted ack register: cleared bits for ack.
* @clear_ack: Use this to set 1 and 0 or vice-versa to clear interrupts. * @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. * @wake_invert: Inverted wake register: cleared bits are wake enabled.
* @type_in_mask: Use the mask registers for controlling irq type. Use this if * @type_in_mask: Use the mask registers for controlling irq type. Use this if
* the hardware provides separate bits for rising/falling edge * the hardware provides separate bits for rising/falling edge
...@@ -1560,18 +1570,20 @@ struct regmap_irq_chip_data; ...@@ -1560,18 +1570,20 @@ struct regmap_irq_chip_data;
* @clear_on_unmask: For chips with interrupts cleared on read: read the status * @clear_on_unmask: For chips with interrupts cleared on read: read the status
* registers before unmasking interrupts to clear any bits * registers before unmasking interrupts to clear any bits
* set when they were masked. * 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 * @not_fixed_stride: Used when chip peripherals are not laid out with fixed
* stride. Must be used with sub_reg_offsets containing the * stride. Must be used with sub_reg_offsets containing the
* offsets to each peripheral. Deprecated; the same thing * offsets to each peripheral. Deprecated; the same thing
* can be accomplished with a @get_irq_reg callback, without * can be accomplished with a @get_irq_reg callback, without
* the need for a @sub_reg_offsets table. * the need for a @sub_reg_offsets table.
* @status_invert: Inverted status register: cleared bits are active interrupts. * @no_status: No status register: all interrupts assumed generated by device.
* @runtime_pm: Hold a runtime PM lock on the device when accessing it.
* *
* @num_regs: Number of registers in each control bank. * @num_regs: Number of registers in each control bank.
*
* @irqs: Descriptors for individual IRQs. Interrupt numbers are * @irqs: Descriptors for individual IRQs. Interrupt numbers are
* assigned based on the index in the array of the interrupt. * assigned based on the index in the array of the interrupt.
* @num_irqs: Number of descriptors. * @num_irqs: Number of descriptors.
*
* @num_type_reg: Number of type registers. Deprecated, use config registers * @num_type_reg: Number of type registers. Deprecated, use config registers
* instead. * instead.
* @num_virt_regs: Number of non-standard irq configuration registers. * @num_virt_regs: Number of non-standard irq configuration registers.
...@@ -1579,6 +1591,7 @@ struct regmap_irq_chip_data; ...@@ -1579,6 +1591,7 @@ struct regmap_irq_chip_data;
* instead. * instead.
* @num_config_bases: Number of config base registers. * @num_config_bases: Number of config base registers.
* @num_config_regs: Number of config registers for each config base register. * @num_config_regs: Number of config registers for each config base register.
*
* @handle_pre_irq: Driver specific callback to handle interrupt from device * @handle_pre_irq: Driver specific callback to handle interrupt from device
* before regmap_irq_handler process the interrupts. * before regmap_irq_handler process the interrupts.
* @handle_post_irq: Driver specific callback to handle interrupt from device * @handle_post_irq: Driver specific callback to handle interrupt from device
...@@ -1625,12 +1638,13 @@ struct regmap_irq_chip { ...@@ -1625,12 +1638,13 @@ struct regmap_irq_chip {
unsigned int use_ack:1; unsigned int use_ack:1;
unsigned int ack_invert:1; unsigned int ack_invert:1;
unsigned int clear_ack:1; unsigned int clear_ack:1;
unsigned int status_invert:1;
unsigned int wake_invert:1; unsigned int wake_invert:1;
unsigned int runtime_pm:1;
unsigned int type_in_mask:1; unsigned int type_in_mask:1;
unsigned int clear_on_unmask:1; unsigned int clear_on_unmask:1;
unsigned int runtime_pm:1;
unsigned int not_fixed_stride:1; unsigned int not_fixed_stride:1;
unsigned int status_invert:1; unsigned int no_status:1;
int num_regs; int num_regs;
...@@ -1650,7 +1664,8 @@ struct regmap_irq_chip { ...@@ -1650,7 +1664,8 @@ struct regmap_irq_chip {
int (*set_type_virt)(unsigned int **buf, unsigned int type, int (*set_type_virt)(unsigned int **buf, unsigned int type,
unsigned long hwirq, int reg); unsigned long hwirq, int reg);
int (*set_type_config)(unsigned int **buf, unsigned int type, 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 (*get_irq_reg)(struct regmap_irq_chip_data *data,
unsigned int base, int index); unsigned int base, int index);
void *irq_drv_data; void *irq_drv_data;
...@@ -1659,7 +1674,8 @@ struct regmap_irq_chip { ...@@ -1659,7 +1674,8 @@ struct regmap_irq_chip {
unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data, unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data,
unsigned int base, int index); unsigned int base, int index);
int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type, 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 regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
int irq_base, const struct regmap_irq_chip *chip, 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