Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
# SPDX-License-Identifier: GPL-2.0
# Generic register map support. There are no user servicable options here,
# this is an API intended to be used by other kernel subsystems. These
# subsystems should select the appropriate symbols.
config REGMAP
bool
default y if (REGMAP_I2C || REGMAP_SLIMBUS || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO || REGMAP_FSI)
help
Enable support for the Register Map (regmap) access API.
Usually, this option is automatically selected when needed.
However, you may want to enable it manually for running the regmap
KUnit tests.
If unsure, say N.
config REGMAP_KUNIT
tristate "KUnit tests for regmap"
depends on KUNIT && REGMAP
default KUNIT_ALL_TESTS
select REGMAP_RAM
config REGMAP_BUILD
bool "Enable regmap build"
depends on KUNIT
select REGMAP
help
This option exists purely to allow the regmap KUnit tests to
be enabled without having to enable some driver that uses
regmap due to unfortunate issues with how KUnit tests are
normally enabled.
config REGMAP_AC97
tristate
config REGMAP_I2C
tristate
depends on I2C
config REGMAP_SLIMBUS
tristate
depends on SLIMBUS
config REGMAP_SPI
tristate
depends on SPI
config REGMAP_SPMI
tristate
depends on SPMI
config REGMAP_W1
tristate
depends on W1
config REGMAP_MDIO
tristate
depends on PHYLIB
config REGMAP_MMIO
tristate
config REGMAP_IRQ
bool
select IRQ_DOMAIN
config REGMAP_RAM
tristate
config REGMAP_SOUNDWIRE
tristate
depends on SOUNDWIRE
config REGMAP_SOUNDWIRE_MBQ
tristate
depends on SOUNDWIRE
config REGMAP_SCCB
tristate
depends on I2C
config REGMAP_I3C
tristate
depends on I3C
config REGMAP_SPI_AVMM
tristate
depends on SPI
config REGMAP_FSI
tristate
depends on FSI
+24
View File
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0
# For include/trace/define_trace.h to include trace.h
CFLAGS_regmap.o := -I$(src)
obj-$(CONFIG_REGMAP) += regmap.o regcache.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 regmap-raw-ram.o
obj-$(CONFIG_REGMAP_SLIMBUS) += regmap-slimbus.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o
obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o
obj-$(CONFIG_REGMAP_W1) += regmap-w1.o
obj-$(CONFIG_REGMAP_SOUNDWIRE) += regmap-sdw.o
obj-$(CONFIG_REGMAP_SOUNDWIRE_MBQ) += regmap-sdw-mbq.o
obj-$(CONFIG_REGMAP_SCCB) += regmap-sccb.o
obj-$(CONFIG_REGMAP_I3C) += regmap-i3c.o
obj-$(CONFIG_REGMAP_SPI_AVMM) += regmap-spi-avmm.o
obj-$(CONFIG_REGMAP_MDIO) += regmap-mdio.o
obj-$(CONFIG_REGMAP_FSI) += regmap-fsi.o
+354
View File
@@ -0,0 +1,354 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Register map access API internal header
*
* Copyright 2011 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*/
#ifndef _REGMAP_INTERNAL_H
#define _REGMAP_INTERNAL_H
#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/wait.h>
struct regmap;
struct regcache_ops;
struct regmap_debugfs_off_cache {
struct list_head list;
off_t min;
off_t max;
unsigned int base_reg;
unsigned int max_reg;
};
struct regmap_format {
size_t buf_size;
size_t reg_bytes;
size_t pad_bytes;
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);
void (*format_val)(void *buf, unsigned int val, unsigned int shift);
unsigned int (*parse_val)(const void *buf);
void (*parse_inplace)(void *buf);
};
struct regmap_async {
struct list_head list;
struct regmap *map;
void *work_buf;
};
struct regmap {
union {
struct mutex mutex;
struct {
spinlock_t spinlock;
unsigned long spinlock_flags;
};
struct {
raw_spinlock_t raw_spinlock;
unsigned long raw_spinlock_flags;
};
};
struct lock_class_key *lock_key;
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg; /* This is passed to lock/unlock functions */
gfp_t alloc_flags;
unsigned int reg_base;
struct device *dev; /* Device we do I/O on */
void *work_buf; /* Scratch buffer used to format I/O */
struct regmap_format format; /* Buffer format */
const struct regmap_bus *bus;
void *bus_context;
const char *name;
spinlock_t async_lock;
wait_queue_head_t async_waitq;
struct list_head async_list;
struct list_head async_free;
int async_ret;
bool async;
#ifdef CONFIG_DEBUG_FS
bool debugfs_disable;
struct dentry *debugfs;
const char *debugfs_name;
int debugfs_dummy_id;
unsigned int debugfs_reg_len;
unsigned int debugfs_val_len;
unsigned int debugfs_tot_len;
struct list_head debugfs_off_cache;
struct mutex cache_lock;
#endif
unsigned int max_register;
bool max_register_is_set;
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
bool (*writeable_noinc_reg)(struct device *dev, unsigned int reg);
bool (*readable_noinc_reg)(struct device *dev, unsigned int reg);
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
const struct regmap_access_table *wr_noinc_table;
const struct regmap_access_table *rd_noinc_table;
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg, unsigned int val);
int (*reg_update_bits)(void *context, unsigned int reg,
unsigned int mask, unsigned int val);
/* Bulk read/write */
int (*read)(void *context, const void *reg_buf, size_t reg_size,
void *val_buf, size_t val_size);
int (*write)(void *context, const void *data, size_t count);
int (*reg_default_cb)(struct device *dev, unsigned int reg,
unsigned int *val);
unsigned long read_flag_mask;
unsigned long write_flag_mask;
/* number of bits to (left) shift the reg value when formatting*/
int reg_shift;
int reg_stride;
int reg_stride_order;
bool defer_caching;
/* If set, will always write field to HW. */
bool force_write_field;
/* regcache specific members */
const struct regcache_ops *cache_ops;
enum regcache_type cache_type;
/* number of bytes in reg_defaults_raw */
unsigned int cache_size_raw;
/* number of bytes per word in reg_defaults_raw */
unsigned int cache_word_size;
/* number of entries in reg_defaults */
unsigned int num_reg_defaults;
/* number of entries in reg_defaults_raw */
unsigned int num_reg_defaults_raw;
/* if set, only the cache is modified not the HW */
bool cache_only;
/* if set, only the HW is modified not the cache */
bool cache_bypass;
/* if set, remember to free reg_defaults_raw */
bool cache_free;
struct reg_default *reg_defaults;
const void *reg_defaults_raw;
void *cache;
/* if set, the cache contains newer data than the HW */
bool cache_dirty;
/* if set, the HW registers are known to match map->reg_defaults */
bool no_sync_defaults;
struct reg_sequence *patch;
unsigned int patch_regs;
/* if set, the regmap core can sleep */
bool can_sleep;
/* if set, converts bulk read to single read */
bool use_single_read;
/* if set, converts bulk write to single write */
bool use_single_write;
/* if set, the device supports multi write mode */
bool can_multi_write;
/* if set, raw reads/writes are limited to this size */
size_t max_raw_read;
size_t max_raw_write;
struct rb_root range_tree;
void *selector_work_buf; /* Scratch buffer used for selector */
struct hwspinlock *hwlock;
};
struct regcache_ops {
const char *name;
enum regcache_type type;
int (*init)(struct regmap *map);
int (*exit)(struct regmap *map);
int (*populate)(struct regmap *map);
#ifdef CONFIG_DEBUG_FS
void (*debugfs_init)(struct regmap *map);
#endif
int (*read)(struct regmap *map, unsigned int reg, unsigned int *value);
int (*write)(struct regmap *map, unsigned int reg, unsigned int value);
int (*sync)(struct regmap *map, unsigned int min, unsigned int max);
int (*drop)(struct regmap *map, unsigned int min, unsigned int max);
};
bool regmap_cached(struct regmap *map, unsigned int reg);
bool regmap_writeable(struct regmap *map, unsigned int reg);
bool regmap_readable(struct regmap *map, unsigned int reg);
bool regmap_volatile(struct regmap *map, unsigned int reg);
bool regmap_precious(struct regmap *map, unsigned int reg);
bool regmap_writeable_noinc(struct regmap *map, unsigned int reg);
bool regmap_readable_noinc(struct regmap *map, unsigned int reg);
int _regmap_write(struct regmap *map, unsigned int reg,
unsigned int val);
struct regmap_range_node {
struct rb_node node;
const char *name;
struct regmap *map;
unsigned int range_min;
unsigned int range_max;
unsigned int selector_reg;
unsigned int selector_mask;
int selector_shift;
unsigned int window_start;
unsigned int window_len;
};
struct regmap_field {
struct regmap *regmap;
unsigned int mask;
/* lsb */
unsigned int shift;
unsigned int reg;
unsigned int id_size;
unsigned int id_offset;
};
#ifdef CONFIG_DEBUG_FS
extern void regmap_debugfs_initcall(void);
extern void regmap_debugfs_init(struct regmap *map);
extern void regmap_debugfs_exit(struct regmap *map);
static inline void regmap_debugfs_disable(struct regmap *map)
{
map->debugfs_disable = true;
}
#else
static inline void regmap_debugfs_initcall(void) { }
static inline void regmap_debugfs_init(struct regmap *map) { }
static inline void regmap_debugfs_exit(struct regmap *map) { }
static inline void regmap_debugfs_disable(struct regmap *map) { }
#endif
/* regcache core declarations */
int regcache_init(struct regmap *map, const struct regmap_config *config);
void regcache_exit(struct regmap *map);
int regcache_read(struct regmap *map,
unsigned int reg, unsigned int *value);
int regcache_write(struct regmap *map,
unsigned int reg, unsigned int value);
int regcache_sync(struct regmap *map);
int regcache_sync_block(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base, unsigned int start,
unsigned int end);
bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg,
unsigned int val);
static inline const void *regcache_get_val_addr(struct regmap *map,
const void *base,
unsigned int idx)
{
return base + (map->cache_word_size * idx);
}
unsigned int regcache_get_val(struct regmap *map, const void *base,
unsigned int idx);
void regcache_set_val(struct regmap *map, void *base, unsigned int idx,
unsigned int val);
int regcache_lookup_reg(struct regmap *map, unsigned int reg);
int regcache_sync_val(struct regmap *map, unsigned int reg, unsigned int val);
int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool noinc);
void regmap_async_complete_cb(struct regmap_async *async, int ret);
enum regmap_endian regmap_get_val_endian(struct device *dev,
const struct regmap_bus *bus,
const struct regmap_config *config);
extern struct regcache_ops regcache_flat_sparse_ops;
extern struct regcache_ops regcache_rbtree_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)
{
if (map->dev)
return dev_name(map->dev);
return map->name;
}
static inline unsigned int regmap_get_offset(const struct regmap *map,
unsigned int index)
{
if (map->reg_stride_order >= 0)
return index << map->reg_stride_order;
else
return index * map->reg_stride;
}
static inline unsigned int regcache_get_index_by_order(const struct regmap *map,
unsigned int reg)
{
return reg >> map->reg_stride_order;
}
struct regmap_ram_data {
unsigned int *vals; /* Allocatd by caller */
bool *read;
bool *written;
enum regmap_endian reg_endian;
bool (*noinc_reg)(struct regmap_ram_data *data, unsigned int reg);
};
/*
* Create a test register map with data stored in RAM, not intended
* for practical use.
*/
struct regmap *__regmap_init_ram(struct device *dev,
const struct regmap_config *config,
struct regmap_ram_data *data,
struct lock_class_key *lock_key,
const char *lock_name);
#define regmap_init_ram(dev, config, data) \
__regmap_lockdep_wrapper(__regmap_init_ram, #dev, dev, config, data)
struct regmap *__regmap_init_raw_ram(struct device *dev,
const struct regmap_config *config,
struct regmap_ram_data *data,
struct lock_class_key *lock_key,
const char *lock_name);
#define regmap_init_raw_ram(dev, config, data) \
__regmap_lockdep_wrapper(__regmap_init_raw_ram, #dev, dev, config, data)
#endif
@@ -0,0 +1,177 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register cache access API - flat caching support
//
// Copyright 2012 Wolfson Microelectronics plc
//
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/limits.h>
#include <linux/overflow.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include "internal.h"
static inline unsigned int regcache_flat_get_index(const struct regmap *map,
unsigned int reg)
{
return regcache_get_index_by_order(map, reg);
}
struct regcache_flat_data {
unsigned long *valid;
unsigned int data[];
};
static int regcache_flat_init(struct regmap *map)
{
unsigned int cache_size;
struct regcache_flat_data *cache;
if (!map || map->reg_stride_order < 0 || !map->max_register_is_set)
return -EINVAL;
cache_size = regcache_flat_get_index(map, map->max_register) + 1;
cache = kzalloc_flex(*cache, data, cache_size, map->alloc_flags);
if (!cache)
return -ENOMEM;
cache->valid = bitmap_zalloc(cache_size, map->alloc_flags);
if (!cache->valid)
goto err_free;
map->cache = cache;
return 0;
err_free:
kfree(cache);
return -ENOMEM;
}
static int regcache_flat_exit(struct regmap *map)
{
struct regcache_flat_data *cache = map->cache;
if (cache)
bitmap_free(cache->valid);
kfree(cache);
map->cache = NULL;
return 0;
}
static int regcache_flat_populate(struct regmap *map)
{
struct regcache_flat_data *cache = map->cache;
unsigned int i;
for (i = 0; i < map->num_reg_defaults; i++) {
unsigned int reg = map->reg_defaults[i].reg;
unsigned int index = regcache_flat_get_index(map, reg);
cache->data[index] = map->reg_defaults[i].def;
__set_bit(index, cache->valid);
}
if (map->reg_default_cb) {
dev_dbg(map->dev,
"Populating regcache_flat using reg_default_cb callback\n");
for (i = 0; i <= map->max_register; i += map->reg_stride) {
unsigned int index = regcache_flat_get_index(map, i);
unsigned int value;
if (test_bit(index, cache->valid))
continue;
if (map->reg_default_cb(map->dev, i, &value))
continue;
cache->data[index] = value;
__set_bit(index, cache->valid);
}
}
return 0;
}
static int regcache_flat_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
struct regcache_flat_data *cache = map->cache;
unsigned int index = regcache_flat_get_index(map, reg);
/* legacy behavior: ignore validity, but warn the user */
if (unlikely(!test_bit(index, cache->valid)))
dev_warn_once(map->dev,
"using zero-initialized flat cache, this may cause unexpected behavior");
*value = cache->data[index];
return 0;
}
static int regcache_flat_sparse_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
struct regcache_flat_data *cache = map->cache;
unsigned int index = regcache_flat_get_index(map, reg);
if (unlikely(!test_bit(index, cache->valid)))
return -ENOENT;
*value = cache->data[index];
return 0;
}
static int regcache_flat_write(struct regmap *map, unsigned int reg,
unsigned int value)
{
struct regcache_flat_data *cache = map->cache;
unsigned int index = regcache_flat_get_index(map, reg);
cache->data[index] = value;
__set_bit(index, cache->valid);
return 0;
}
static int regcache_flat_drop(struct regmap *map, unsigned int min,
unsigned int max)
{
struct regcache_flat_data *cache = map->cache;
unsigned int bitmap_min = regcache_flat_get_index(map, min);
unsigned int bitmap_max = regcache_flat_get_index(map, max);
bitmap_clear(cache->valid, bitmap_min, bitmap_max + 1 - bitmap_min);
return 0;
}
struct regcache_ops regcache_flat_ops = {
.type = REGCACHE_FLAT,
.name = "flat",
.init = regcache_flat_init,
.exit = regcache_flat_exit,
.populate = regcache_flat_populate,
.read = regcache_flat_read,
.write = regcache_flat_write,
};
struct regcache_ops regcache_flat_sparse_ops = {
.type = REGCACHE_FLAT_S,
.name = "flat-sparse",
.init = regcache_flat_init,
.exit = regcache_flat_exit,
.populate = regcache_flat_populate,
.read = regcache_flat_sparse_read,
.write = regcache_flat_write,
.drop = regcache_flat_drop,
};
@@ -0,0 +1,396 @@
// 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_array(last - index + 1, sizeof(*entry), map->alloc_flags);
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, map->alloc_flags);
mas_unlock(&mas);
if (ret) {
kfree(entry);
return ret;
}
kfree(lower);
kfree(upper);
return 0;
}
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;
/* initialized to work around false-positive -Wuninitialized warning */
unsigned long lower_index = 0, lower_last = 0;
unsigned long upper_index, upper_last;
int ret = 0;
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_array(entry,
min - mas.index, sizeof(*lower),
map->alloc_flags);
if (!lower) {
ret = -ENOMEM;
goto out_unlocked;
}
}
if (mas.last > max) {
upper_index = max + 1;
upper_last = mas.last;
upper = kmemdup_array(&entry[max - mas.index + 1],
mas.last - max, sizeof(*upper),
map->alloc_flags);
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, map->alloc_flags);
if (ret != 0)
goto out;
lower = NULL;
}
if (upper) {
mas_set_range(&mas, upper_index, upper_last);
ret = mas_store_gfp(&mas, upper, map->alloc_flags);
if (ret != 0)
goto out;
upper = NULL;
}
}
out:
mas_unlock(&mas);
out_unlocked:
kfree(lower);
kfree(upper);
return ret;
}
static int regcache_maple_sync_block(struct regmap *map, unsigned long *entry,
struct ma_state *mas,
unsigned int min, unsigned int max)
{
void *buf;
unsigned long r;
size_t val_bytes = map->format.val_bytes;
int ret = 0;
mas_pause(mas);
rcu_read_unlock();
/*
* Use a raw write if writing more than one register to a
* device that supports raw writes to reduce transaction
* overheads.
*/
if (max - min > 1 && regmap_can_raw_write(map)) {
buf = kmalloc_array(max - min, val_bytes, map->alloc_flags);
if (!buf) {
ret = -ENOMEM;
goto out;
}
/* Render the data for a raw write */
for (r = min; r < max; r++) {
regcache_set_val(map, buf, r - min,
entry[r - mas->index]);
}
ret = _regmap_raw_write(map, min, buf, (max - min) * val_bytes,
false);
kfree(buf);
} else {
for (r = min; r < max; r++) {
ret = _regmap_write(map, r,
entry[r - mas->index]);
if (ret != 0)
goto out;
}
}
out:
rcu_read_lock();
return ret;
}
static int regcache_maple_sync(struct regmap *map, unsigned int min,
unsigned int max)
{
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, v, sync_start;
int ret = 0;
bool sync_needed = false;
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++) {
v = entry[r - mas.index];
if (regcache_reg_needs_sync(map, r, v)) {
if (!sync_needed) {
sync_start = r;
sync_needed = true;
}
continue;
}
if (!sync_needed)
continue;
ret = regcache_maple_sync_block(map, entry, &mas,
sync_start, r);
if (ret != 0)
goto out;
sync_needed = false;
}
if (sync_needed) {
ret = regcache_maple_sync_block(map, entry, &mas,
sync_start, r);
if (ret != 0)
goto out;
sync_needed = false;
}
}
out:
rcu_read_unlock();
map->cache_bypass = false;
return ret;
}
static int regcache_maple_init(struct regmap *map)
{
struct maple_tree *mt;
mt = kmalloc_obj(*mt, map->alloc_flags);
if (!mt)
return -ENOMEM;
map->cache = mt;
mt_init(mt);
if (!mt_external_lock(mt) && map->lock_key)
lockdep_set_class_and_subclass(&mt->ma_lock, map->lock_key, 1);
return 0;
}
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_insert_block(struct regmap *map, int first,
int last)
{
struct maple_tree *mt = map->cache;
MA_STATE(mas, mt, first, last);
unsigned long *entry;
int i, ret;
entry = kmalloc_array(last - first + 1, sizeof(*entry), map->alloc_flags);
if (!entry)
return -ENOMEM;
for (i = 0; i < last - first + 1; i++)
entry[i] = map->reg_defaults[first + i].def;
mas_lock(&mas);
mas_set_range(&mas, map->reg_defaults[first].reg,
map->reg_defaults[last].reg);
ret = mas_store_gfp(&mas, entry, map->alloc_flags);
mas_unlock(&mas);
if (ret)
kfree(entry);
return ret;
}
static int regcache_maple_populate(struct regmap *map)
{
int i;
int ret;
int range_start;
range_start = 0;
/* Scan for ranges of contiguous registers */
for (i = 1; i < map->num_reg_defaults; i++) {
if (map->reg_defaults[i].reg !=
map->reg_defaults[i - 1].reg + 1) {
ret = regcache_maple_insert_block(map, range_start,
i - 1);
if (ret != 0)
return ret;
range_start = i;
}
}
/* Add the last block */
return regcache_maple_insert_block(map, range_start, map->num_reg_defaults - 1);
}
struct regcache_ops regcache_maple_ops = {
.type = REGCACHE_MAPLE,
.name = "maple",
.init = regcache_maple_init,
.exit = regcache_maple_exit,
.populate = regcache_maple_populate,
.read = regcache_maple_read,
.write = regcache_maple_write,
.drop = regcache_maple_drop,
.sync = regcache_maple_sync,
};
@@ -0,0 +1,559 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register cache access API - rbtree caching support
//
// Copyright 2011 Wolfson Microelectronics plc
//
// Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/rbtree.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include "internal.h"
static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
unsigned int value);
static int regcache_rbtree_exit(struct regmap *map);
struct regcache_rbtree_node {
/* block of adjacent registers */
void *block;
/* Which registers are present */
unsigned long *cache_present;
/* base register handled by this block */
unsigned int base_reg;
/* number of registers available in the block */
unsigned int blklen;
/* the actual rbtree node holding this block */
struct rb_node node;
};
struct regcache_rbtree_ctx {
struct rb_root root;
struct regcache_rbtree_node *cached_rbnode;
};
static inline void regcache_rbtree_get_base_top_reg(
struct regmap *map,
struct regcache_rbtree_node *rbnode,
unsigned int *base, unsigned int *top)
{
*base = rbnode->base_reg;
*top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride);
}
static unsigned int regcache_rbtree_get_register(struct regmap *map,
struct regcache_rbtree_node *rbnode, unsigned int idx)
{
return regcache_get_val(map, rbnode->block, idx);
}
static void regcache_rbtree_set_register(struct regmap *map,
struct regcache_rbtree_node *rbnode,
unsigned int idx, unsigned int val)
{
set_bit(idx, rbnode->cache_present);
regcache_set_val(map, rbnode->block, idx, val);
}
static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
unsigned int reg)
{
struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
struct rb_node *node;
struct regcache_rbtree_node *rbnode;
unsigned int base_reg, top_reg;
rbnode = rbtree_ctx->cached_rbnode;
if (rbnode) {
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
&top_reg);
if (reg >= base_reg && reg <= top_reg)
return rbnode;
}
node = rbtree_ctx->root.rb_node;
while (node) {
rbnode = rb_entry(node, struct regcache_rbtree_node, node);
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
&top_reg);
if (reg >= base_reg && reg <= top_reg) {
rbtree_ctx->cached_rbnode = rbnode;
return rbnode;
} else if (reg > top_reg) {
node = node->rb_right;
} else if (reg < base_reg) {
node = node->rb_left;
}
}
return NULL;
}
static int regcache_rbtree_insert(struct regmap *map, struct rb_root *root,
struct regcache_rbtree_node *rbnode)
{
struct rb_node **new, *parent;
struct regcache_rbtree_node *rbnode_tmp;
unsigned int base_reg_tmp, top_reg_tmp;
unsigned int base_reg;
parent = NULL;
new = &root->rb_node;
while (*new) {
rbnode_tmp = rb_entry(*new, struct regcache_rbtree_node, node);
/* base and top registers of the current rbnode */
regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp,
&top_reg_tmp);
/* base register of the rbnode to be added */
base_reg = rbnode->base_reg;
parent = *new;
/* if this register has already been inserted, just return */
if (base_reg >= base_reg_tmp &&
base_reg <= top_reg_tmp)
return 0;
else if (base_reg > top_reg_tmp)
new = &((*new)->rb_right);
else if (base_reg < base_reg_tmp)
new = &((*new)->rb_left);
}
/* insert the node into the rbtree */
rb_link_node(&rbnode->node, parent, new);
rb_insert_color(&rbnode->node, root);
return 1;
}
#ifdef CONFIG_DEBUG_FS
static int rbtree_show(struct seq_file *s, void *ignored)
{
struct regmap *map = s->private;
struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
struct regcache_rbtree_node *n;
struct rb_node *node;
unsigned int base, top;
size_t mem_size;
int nodes = 0;
int registers = 0;
int this_registers, average;
map->lock(map->lock_arg);
mem_size = sizeof(*rbtree_ctx);
for (node = rb_first(&rbtree_ctx->root); node != NULL;
node = rb_next(node)) {
n = rb_entry(node, struct regcache_rbtree_node, node);
mem_size += sizeof(*n);
mem_size += (n->blklen * map->cache_word_size);
mem_size += BITS_TO_LONGS(n->blklen) * sizeof(long);
regcache_rbtree_get_base_top_reg(map, n, &base, &top);
this_registers = ((top - base) / map->reg_stride) + 1;
seq_printf(s, "%x-%x (%d)\n", base, top, this_registers);
nodes++;
registers += this_registers;
}
if (nodes)
average = registers / nodes;
else
average = 0;
seq_printf(s, "%d nodes, %d registers, average %d registers, used %zu bytes\n",
nodes, registers, average, mem_size);
map->unlock(map->lock_arg);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(rbtree);
static void rbtree_debugfs_init(struct regmap *map)
{
debugfs_create_file("rbtree", 0400, map->debugfs, map, &rbtree_fops);
}
#endif
static int regcache_rbtree_init(struct regmap *map)
{
struct regcache_rbtree_ctx *rbtree_ctx;
map->cache = kmalloc_obj(*rbtree_ctx, map->alloc_flags);
if (!map->cache)
return -ENOMEM;
rbtree_ctx = map->cache;
rbtree_ctx->root = RB_ROOT;
rbtree_ctx->cached_rbnode = NULL;
return 0;
}
static int regcache_rbtree_exit(struct regmap *map)
{
struct rb_node *next;
struct regcache_rbtree_ctx *rbtree_ctx;
struct regcache_rbtree_node *rbtree_node;
/* if we've already been called then just return */
rbtree_ctx = map->cache;
if (!rbtree_ctx)
return 0;
/* free up the rbtree */
next = rb_first(&rbtree_ctx->root);
while (next) {
rbtree_node = rb_entry(next, struct regcache_rbtree_node, node);
next = rb_next(&rbtree_node->node);
rb_erase(&rbtree_node->node, &rbtree_ctx->root);
kfree(rbtree_node->cache_present);
kfree(rbtree_node->block);
kfree(rbtree_node);
}
/* release the resources */
kfree(map->cache);
map->cache = NULL;
return 0;
}
static int regcache_rbtree_populate(struct regmap *map)
{
unsigned int i;
int ret;
for (i = 0; i < map->num_reg_defaults; i++) {
ret = regcache_rbtree_write(map,
map->reg_defaults[i].reg,
map->reg_defaults[i].def);
if (ret)
return ret;
}
return 0;
}
static int regcache_rbtree_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
struct regcache_rbtree_node *rbnode;
unsigned int reg_tmp;
rbnode = regcache_rbtree_lookup(map, reg);
if (rbnode) {
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
if (!test_bit(reg_tmp, rbnode->cache_present))
return -ENOENT;
*value = regcache_rbtree_get_register(map, rbnode, reg_tmp);
} else {
return -ENOENT;
}
return 0;
}
static int regcache_rbtree_insert_to_block(struct regmap *map,
struct regcache_rbtree_node *rbnode,
unsigned int base_reg,
unsigned int top_reg,
unsigned int reg,
unsigned int value)
{
unsigned int blklen;
unsigned int pos, offset;
unsigned long *present;
u8 *blk;
blklen = (top_reg - base_reg) / map->reg_stride + 1;
pos = (reg - base_reg) / map->reg_stride;
offset = (rbnode->base_reg - base_reg) / map->reg_stride;
blk = krealloc_array(rbnode->block, blklen, map->cache_word_size, map->alloc_flags);
if (!blk)
return -ENOMEM;
rbnode->block = blk;
if (BITS_TO_LONGS(blklen) > BITS_TO_LONGS(rbnode->blklen)) {
present = krealloc_array(rbnode->cache_present,
BITS_TO_LONGS(blklen), sizeof(*present),
map->alloc_flags);
if (!present)
return -ENOMEM;
memset(present + BITS_TO_LONGS(rbnode->blklen), 0,
(BITS_TO_LONGS(blklen) - BITS_TO_LONGS(rbnode->blklen))
* sizeof(*present));
} else {
present = rbnode->cache_present;
}
/* insert the register value in the correct place in the rbnode block */
if (pos == 0) {
memmove(blk + offset * map->cache_word_size,
blk, rbnode->blklen * map->cache_word_size);
bitmap_shift_left(present, present, offset, blklen);
}
/* update the rbnode block, its size and the base register */
rbnode->blklen = blklen;
rbnode->base_reg = base_reg;
rbnode->cache_present = present;
regcache_rbtree_set_register(map, rbnode, pos, value);
return 0;
}
static struct regcache_rbtree_node *
regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
{
struct regcache_rbtree_node *rbnode;
const struct regmap_range *range;
int i;
rbnode = kzalloc_obj(*rbnode, map->alloc_flags);
if (!rbnode)
return NULL;
/* If there is a read table then use it to guess at an allocation */
if (map->rd_table) {
for (i = 0; i < map->rd_table->n_yes_ranges; i++) {
if (regmap_reg_in_range(reg,
&map->rd_table->yes_ranges[i]))
break;
}
if (i != map->rd_table->n_yes_ranges) {
range = &map->rd_table->yes_ranges[i];
rbnode->blklen = (range->range_max - range->range_min) /
map->reg_stride + 1;
rbnode->base_reg = range->range_min;
}
}
if (!rbnode->blklen) {
rbnode->blklen = 1;
rbnode->base_reg = reg;
}
rbnode->block = kmalloc_array(rbnode->blklen, map->cache_word_size,
map->alloc_flags);
if (!rbnode->block)
goto err_free;
rbnode->cache_present = kcalloc(BITS_TO_LONGS(rbnode->blklen),
sizeof(*rbnode->cache_present),
map->alloc_flags);
if (!rbnode->cache_present)
goto err_free_block;
return rbnode;
err_free_block:
kfree(rbnode->block);
err_free:
kfree(rbnode);
return NULL;
}
static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
unsigned int value)
{
struct regcache_rbtree_ctx *rbtree_ctx;
struct regcache_rbtree_node *rbnode, *rbnode_tmp;
struct rb_node *node;
unsigned int reg_tmp;
int ret;
rbtree_ctx = map->cache;
/* if we can't locate it in the cached rbnode we'll have
* to traverse the rbtree looking for it.
*/
rbnode = regcache_rbtree_lookup(map, reg);
if (rbnode) {
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
regcache_rbtree_set_register(map, rbnode, reg_tmp, value);
} else {
unsigned int base_reg, top_reg;
unsigned int new_base_reg, new_top_reg;
unsigned int min, max;
unsigned int max_dist;
unsigned int dist, best_dist = UINT_MAX;
max_dist = map->reg_stride * sizeof(*rbnode_tmp) /
map->cache_word_size;
if (reg < max_dist)
min = 0;
else
min = reg - max_dist;
max = reg + max_dist;
/* look for an adjacent register to the one we are about to add */
node = rbtree_ctx->root.rb_node;
while (node) {
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
node);
regcache_rbtree_get_base_top_reg(map, rbnode_tmp,
&base_reg, &top_reg);
if (base_reg <= max && top_reg >= min) {
if (reg < base_reg)
dist = base_reg - reg;
else if (reg > top_reg)
dist = reg - top_reg;
else
dist = 0;
if (dist < best_dist) {
rbnode = rbnode_tmp;
best_dist = dist;
new_base_reg = min(reg, base_reg);
new_top_reg = max(reg, top_reg);
}
}
/*
* Keep looking, we want to choose the closest block,
* otherwise we might end up creating overlapping
* blocks, which breaks the rbtree.
*/
if (reg < base_reg)
node = node->rb_left;
else if (reg > top_reg)
node = node->rb_right;
else
break;
}
if (rbnode) {
ret = regcache_rbtree_insert_to_block(map, rbnode,
new_base_reg,
new_top_reg, reg,
value);
if (ret)
return ret;
rbtree_ctx->cached_rbnode = rbnode;
return 0;
}
/* We did not manage to find a place to insert it in
* an existing block so create a new rbnode.
*/
rbnode = regcache_rbtree_node_alloc(map, reg);
if (!rbnode)
return -ENOMEM;
regcache_rbtree_set_register(map, rbnode,
(reg - rbnode->base_reg) / map->reg_stride,
value);
regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode);
rbtree_ctx->cached_rbnode = rbnode;
}
return 0;
}
static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
unsigned int max)
{
struct regcache_rbtree_ctx *rbtree_ctx;
struct rb_node *node;
struct regcache_rbtree_node *rbnode;
unsigned int base_reg, top_reg;
unsigned int start, end;
int ret;
map->async = true;
rbtree_ctx = map->cache;
for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
rbnode = rb_entry(node, struct regcache_rbtree_node, node);
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
&top_reg);
if (base_reg > max)
break;
if (top_reg < min)
continue;
if (min > base_reg)
start = (min - base_reg) / map->reg_stride;
else
start = 0;
if (max < top_reg)
end = (max - base_reg) / map->reg_stride + 1;
else
end = rbnode->blklen;
ret = regcache_sync_block(map, rbnode->block,
rbnode->cache_present,
rbnode->base_reg, start, end);
if (ret != 0)
return ret;
}
map->async = false;
return regmap_async_complete(map);
}
static int regcache_rbtree_drop(struct regmap *map, unsigned int min,
unsigned int max)
{
struct regcache_rbtree_ctx *rbtree_ctx;
struct regcache_rbtree_node *rbnode;
struct rb_node *node;
unsigned int base_reg, top_reg;
unsigned int start, end;
rbtree_ctx = map->cache;
for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
rbnode = rb_entry(node, struct regcache_rbtree_node, node);
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
&top_reg);
if (base_reg > max)
break;
if (top_reg < min)
continue;
if (min > base_reg)
start = (min - base_reg) / map->reg_stride;
else
start = 0;
if (max < top_reg)
end = (max - base_reg) / map->reg_stride + 1;
else
end = rbnode->blklen;
bitmap_clear(rbnode->cache_present, start, end - start);
}
return 0;
}
struct regcache_ops regcache_rbtree_ops = {
.type = REGCACHE_RBTREE,
.name = "rbtree",
.init = regcache_rbtree_init,
.exit = regcache_rbtree_exit,
.populate = regcache_rbtree_populate,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = rbtree_debugfs_init,
#endif
.read = regcache_rbtree_read,
.write = regcache_rbtree_write,
.sync = regcache_rbtree_sync,
.drop = regcache_rbtree_drop,
};
+899
View File
@@ -0,0 +1,899 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register cache access API
//
// Copyright 2011 Wolfson Microelectronics plc
//
// Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
#include <linux/bsearch.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include "trace.h"
#include "internal.h"
static const struct regcache_ops *cache_types[] = {
&regcache_flat_sparse_ops,
&regcache_rbtree_ops,
&regcache_maple_ops,
&regcache_flat_ops,
};
static int regcache_defaults_cmp(const void *a, const void *b)
{
const struct reg_default *x = a;
const struct reg_default *y = b;
if (x->reg > y->reg)
return 1;
else if (x->reg < y->reg)
return -1;
else
return 0;
}
void regcache_sort_defaults(struct reg_default *defaults, unsigned int ndefaults)
{
sort(defaults, ndefaults, sizeof(*defaults),
regcache_defaults_cmp, NULL);
}
EXPORT_SYMBOL_GPL(regcache_sort_defaults);
static int regcache_count_cacheable_registers(struct regmap *map)
{
unsigned int count;
/* calculate the size of reg_defaults */
count = 0;
for (unsigned int i = 0; i < map->num_reg_defaults_raw; i++)
if (regmap_readable(map, i * map->reg_stride) &&
!regmap_volatile(map, i * map->reg_stride))
count++;
return count;
}
static int regcache_hw_init(struct regmap *map)
{
int ret;
unsigned int reg, val;
void *tmp_buf;
if (!map->reg_defaults_raw) {
bool cache_bypass = map->cache_bypass;
dev_dbg(map->dev, "No cache defaults, reading back from HW\n");
/* Bypass the cache access till data read from HW */
map->cache_bypass = true;
tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL);
if (!tmp_buf)
return -ENOMEM;
ret = regmap_raw_read(map, 0, tmp_buf,
map->cache_size_raw);
map->cache_bypass = cache_bypass;
if (ret == 0) {
map->reg_defaults_raw = tmp_buf;
map->cache_free = true;
} else {
kfree(tmp_buf);
}
}
/* fill the reg_defaults */
for (unsigned int i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
reg = i * map->reg_stride;
if (!regmap_readable(map, reg))
continue;
if (regmap_volatile(map, reg))
continue;
if (map->reg_defaults_raw) {
val = regcache_get_val(map, map->reg_defaults_raw, i);
} else {
bool cache_bypass = map->cache_bypass;
map->cache_bypass = true;
ret = regmap_read(map, reg, &val);
map->cache_bypass = cache_bypass;
if (ret != 0) {
dev_err(map->dev, "Failed to read %x: %d\n",
reg, ret);
return ret;
}
}
map->reg_defaults[j].reg = reg;
map->reg_defaults[j].def = val;
j++;
}
return 0;
}
static void regcache_hw_exit(struct regmap *map)
{
if (map->cache_free)
kfree(map->reg_defaults_raw);
}
int regcache_init(struct regmap *map, const struct regmap_config *config)
{
bool sort_defaults = false;
unsigned int reg_prev = 0;
int count = 0;
int ret;
int i;
void *tmp_buf;
if (map->cache_type == REGCACHE_NONE) {
if (config->reg_defaults || config->num_reg_defaults_raw)
dev_warn(map->dev,
"No cache used with register defaults set!\n");
map->cache_bypass = true;
return 0;
}
if (config->reg_defaults && !config->num_reg_defaults) {
dev_err(map->dev,
"Register defaults are set without the number!\n");
return -EINVAL;
}
if (config->num_reg_defaults && !config->reg_defaults) {
dev_err(map->dev,
"Register defaults number are set without the reg!\n");
return -EINVAL;
}
for (i = 0; i < config->num_reg_defaults; i++) {
if (config->reg_defaults[i].reg % map->reg_stride)
return -EINVAL;
if (reg_prev > config->reg_defaults[i].reg)
sort_defaults = true;
reg_prev = config->reg_defaults[i].reg;
}
for (i = 0; i < ARRAY_SIZE(cache_types); i++)
if (cache_types[i]->type == map->cache_type)
break;
if (i == ARRAY_SIZE(cache_types)) {
dev_err(map->dev, "Could not match cache type: %d\n",
map->cache_type);
return -EINVAL;
}
map->num_reg_defaults = config->num_reg_defaults;
map->num_reg_defaults_raw = config->num_reg_defaults_raw;
map->reg_defaults_raw = config->reg_defaults_raw;
map->cache_word_size = BITS_TO_BYTES(config->val_bits);
map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw;
map->cache = NULL;
map->cache_ops = cache_types[i];
if (!map->cache_ops->read ||
!map->cache_ops->write ||
!map->cache_ops->name)
return -EINVAL;
/* We still need to ensure that the reg_defaults
* won't vanish from under us. We'll need to make
* a copy of it.
*/
if (config->reg_defaults) {
tmp_buf = kmemdup_array(config->reg_defaults, map->num_reg_defaults,
sizeof(*map->reg_defaults), GFP_KERNEL);
if (!tmp_buf)
return -ENOMEM;
/* regcache_lookup_reg() bsearch()es this array */
if (sort_defaults) {
dev_warn(map->dev,
"Driver needs fixing: Unsorted reg_defaults, sorting the copy\n");
regcache_sort_defaults(tmp_buf, map->num_reg_defaults);
}
map->reg_defaults = tmp_buf;
} else if (map->num_reg_defaults_raw) {
count = regcache_count_cacheable_registers(map);
if (!count)
map->cache_bypass = true;
/* All registers are unreadable or volatile, so just bypass */
if (map->cache_bypass)
return 0;
map->num_reg_defaults = count;
map->reg_defaults = kmalloc_objs(struct reg_default, count);
if (!map->reg_defaults)
return -ENOMEM;
}
if (!map->max_register_is_set && map->num_reg_defaults_raw) {
map->max_register = (map->num_reg_defaults_raw - 1) * map->reg_stride;
map->max_register_is_set = true;
}
if (map->cache_ops->init) {
dev_dbg(map->dev, "Initializing %s cache\n",
map->cache_ops->name);
map->lock(map->lock_arg);
ret = map->cache_ops->init(map);
map->unlock(map->lock_arg);
if (ret)
goto err_free_reg_defaults;
}
/*
* Some devices such as PMICs don't have cache defaults,
* we cope with this by reading back the HW registers and
* crafting the cache defaults by hand.
*/
if (count) {
ret = regcache_hw_init(map);
if (ret)
goto err_exit;
}
if (map->cache_ops->populate &&
(map->num_reg_defaults || map->reg_default_cb)) {
dev_dbg(map->dev, "Populating %s cache\n", map->cache_ops->name);
map->lock(map->lock_arg);
ret = map->cache_ops->populate(map);
map->unlock(map->lock_arg);
if (ret)
goto err_free;
}
return 0;
err_free:
regcache_hw_exit(map);
err_exit:
if (map->cache_ops->exit) {
dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name);
map->lock(map->lock_arg);
map->cache_ops->exit(map);
map->unlock(map->lock_arg);
}
err_free_reg_defaults:
kfree(map->reg_defaults);
return ret;
}
void regcache_exit(struct regmap *map)
{
if (map->cache_type == REGCACHE_NONE)
return;
BUG_ON(!map->cache_ops);
regcache_hw_exit(map);
if (map->cache_ops->exit) {
dev_dbg(map->dev, "Destroying %s cache\n",
map->cache_ops->name);
map->lock(map->lock_arg);
map->cache_ops->exit(map);
map->unlock(map->lock_arg);
}
kfree(map->reg_defaults);
}
/**
* regcache_read - Fetch the value of a given register from the cache.
*
* @map: map to configure.
* @reg: The register index.
* @value: The value to be returned.
*
* Return a negative value on failure, 0 on success.
*/
int regcache_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
int ret;
if (map->cache_type == REGCACHE_NONE)
return -EINVAL;
BUG_ON(!map->cache_ops);
if (!regmap_volatile(map, reg)) {
ret = map->cache_ops->read(map, reg, value);
if (ret == 0)
trace_regmap_reg_read_cache(map, reg, *value);
return ret;
}
return -EINVAL;
}
/**
* regcache_write - Set the value of a given register in the cache.
*
* @map: map to configure.
* @reg: The register index.
* @value: The new register value.
*
* Return a negative value on failure, 0 on success.
*/
int regcache_write(struct regmap *map,
unsigned int reg, unsigned int value)
{
if (map->cache_type == REGCACHE_NONE)
return 0;
BUG_ON(!map->cache_ops);
if (!regmap_volatile(map, reg))
return map->cache_ops->write(map, reg, value);
return 0;
}
bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg,
unsigned int val)
{
int ret;
if (!regmap_writeable(map, reg))
return false;
/* If we don't know the chip just got reset, then sync everything. */
if (!map->no_sync_defaults)
return true;
/* Is this the hardware default? If so skip. */
ret = regcache_lookup_reg(map, reg);
if (ret >= 0 && val == map->reg_defaults[ret].def)
return false;
return true;
}
static int regcache_default_sync(struct regmap *map, unsigned int min,
unsigned int max)
{
unsigned int reg;
for (reg = min; reg <= max; reg += map->reg_stride) {
unsigned int val;
int ret;
if (regmap_volatile(map, reg) ||
!regmap_writeable(map, reg))
continue;
ret = regcache_read(map, reg, &val);
if (ret == -ENOENT)
continue;
if (ret)
return ret;
if (!regcache_reg_needs_sync(map, reg, val))
continue;
map->cache_bypass = true;
ret = _regmap_write(map, reg, val);
map->cache_bypass = false;
if (ret) {
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 rbtree_all(const void *key, const struct rb_node *node)
{
return 0;
}
/**
* regcache_sync - Sync the register cache with the hardware.
*
* @map: map to configure.
*
* Any registers that should not be synced should be marked as
* volatile. In general drivers can choose not to use the provided
* syncing functionality if they so require.
*
* Return a negative value on failure, 0 on success.
*/
int regcache_sync(struct regmap *map)
{
int ret = 0;
unsigned int i;
const char *name;
bool bypass;
struct rb_node *node;
if (WARN_ON(map->cache_type == REGCACHE_NONE))
return -EINVAL;
BUG_ON(!map->cache_ops);
map->lock(map->lock_arg);
/* Remember the initial bypass state */
bypass = map->cache_bypass;
dev_dbg(map->dev, "Syncing %s cache\n",
map->cache_ops->name);
name = map->cache_ops->name;
trace_regcache_sync(map, name, "start");
if (!map->cache_dirty)
goto out;
/* Apply any patch first */
map->cache_bypass = true;
for (i = 0; i < map->patch_regs; i++) {
ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def);
if (ret != 0) {
dev_err(map->dev, "Failed to write %x = %x: %d\n",
map->patch[i].reg, map->patch[i].def, ret);
goto out;
}
}
map->cache_bypass = false;
if (map->cache_ops->sync)
ret = map->cache_ops->sync(map, 0, map->max_register);
else
ret = regcache_default_sync(map, 0, map->max_register);
if (ret == 0)
map->cache_dirty = false;
out:
/* Restore the bypass state */
map->cache_bypass = bypass;
map->no_sync_defaults = false;
/*
* If we did any paging with cache bypassed and a cached
* paging register then the register and cache state might
* have gone out of sync, force writes of all the paging
* registers.
*/
rb_for_each(node, NULL, &map->range_tree, rbtree_all) {
struct regmap_range_node *this =
rb_entry(node, struct regmap_range_node, node);
/* If there's nothing in the cache there's nothing to sync */
if (regcache_read(map, this->selector_reg, &i) != 0)
continue;
ret = _regmap_write(map, this->selector_reg, i);
if (ret != 0) {
dev_err(map->dev, "Failed to write %x = %x: %d\n",
this->selector_reg, i, ret);
break;
}
}
map->unlock(map->lock_arg);
regmap_async_complete(map);
trace_regcache_sync(map, name, "stop");
return ret;
}
EXPORT_SYMBOL_GPL(regcache_sync);
/**
* regcache_sync_region - Sync part of the register cache with the hardware.
*
* @map: map to sync.
* @min: first register to sync
* @max: last register to sync
*
* Write all non-default register values in the specified region to
* the hardware.
*
* Return a negative value on failure, 0 on success.
*/
int regcache_sync_region(struct regmap *map, unsigned int min,
unsigned int max)
{
int ret = 0;
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);
/* Remember the initial bypass state */
bypass = map->cache_bypass;
name = map->cache_ops->name;
dev_dbg(map->dev, "Syncing %s cache from %#x-%#x\n", name, min, max);
trace_regcache_sync(map, name, "start region");
if (!map->cache_dirty)
goto out;
map->async = true;
if (map->cache_ops->sync)
ret = map->cache_ops->sync(map, min, max);
else
ret = regcache_default_sync(map, min, max);
out:
/* Restore the bypass state */
map->cache_bypass = bypass;
map->async = false;
map->no_sync_defaults = false;
map->unlock(map->lock_arg);
regmap_async_complete(map);
trace_regcache_sync(map, name, "stop region");
return ret;
}
EXPORT_SYMBOL_GPL(regcache_sync_region);
/**
* regcache_drop_region - Discard part of the register cache
*
* @map: map to operate on
* @min: first register to discard
* @max: last register to discard
*
* Discard part of the register cache.
*
* Return a negative value on failure, 0 on success.
*/
int regcache_drop_region(struct regmap *map, unsigned int min,
unsigned int max)
{
int ret = 0;
if (!map->cache_ops || !map->cache_ops->drop)
return -EINVAL;
map->lock(map->lock_arg);
trace_regcache_drop_region(map, min, max);
ret = map->cache_ops->drop(map, min, max);
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regcache_drop_region);
/**
* regcache_cache_only - Put a register map into cache only mode
*
* @map: map to configure
* @enable: flag if changes should be written to the hardware
*
* When a register map is marked as cache only writes to the register
* map API will only update the register cache, they will not cause
* any hardware changes. This is useful for allowing portions of
* drivers to act as though the device were functioning as normal when
* it is disabled for power saving reasons.
*/
void regcache_cache_only(struct regmap *map, bool enable)
{
map->lock(map->lock_arg);
WARN_ON(map->cache_type != REGCACHE_NONE &&
map->cache_bypass && enable);
map->cache_only = enable;
trace_regmap_cache_only(map, enable);
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_cache_only);
/**
* regcache_mark_dirty - Indicate that HW registers were reset to default values
*
* @map: map to mark
*
* Inform regcache that the device has been powered down or reset, so that
* on resume, regcache_sync() knows to write out all non-default values
* stored in the cache.
*
* If this function is not called, regcache_sync() will assume that
* the hardware state still matches the cache state, modulo any writes that
* happened when cache_only was true.
*/
void regcache_mark_dirty(struct regmap *map)
{
map->lock(map->lock_arg);
map->cache_dirty = true;
map->no_sync_defaults = true;
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_mark_dirty);
/**
* regcache_cache_bypass - Put a register map into cache bypass mode
*
* @map: map to configure
* @enable: flag if changes should not be written to the cache
*
* When a register map is marked with the cache bypass option, writes
* to the register map API will only update the hardware and not
* the cache directly. This is useful when syncing the cache back to
* the hardware.
*/
void regcache_cache_bypass(struct regmap *map, bool enable)
{
map->lock(map->lock_arg);
WARN_ON(map->cache_only && enable);
map->cache_bypass = enable;
trace_regmap_cache_bypass(map, enable);
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
/**
* regcache_reg_cached - Check if a register is cached
*
* @map: map to check
* @reg: register to check
*
* Reports if a register is cached.
*/
bool regcache_reg_cached(struct regmap *map, unsigned int reg)
{
unsigned int val;
int ret;
map->lock(map->lock_arg);
ret = regcache_read(map, reg, &val);
map->unlock(map->lock_arg);
return ret == 0;
}
EXPORT_SYMBOL_GPL(regcache_reg_cached);
void regcache_set_val(struct regmap *map, void *base, unsigned int idx,
unsigned int val)
{
/* Use device native format if possible */
if (map->format.format_val) {
map->format.format_val(base + (map->cache_word_size * idx),
val, 0);
return;
}
switch (map->cache_word_size) {
case 1: {
u8 *cache = base;
cache[idx] = val;
break;
}
case 2: {
u16 *cache = base;
cache[idx] = val;
break;
}
case 4: {
u32 *cache = base;
cache[idx] = val;
break;
}
default:
BUG();
}
}
unsigned int regcache_get_val(struct regmap *map, const void *base,
unsigned int idx)
{
if (!base)
return -EINVAL;
/* Use device native format if possible */
if (map->format.parse_val)
return map->format.parse_val(regcache_get_val_addr(map, base,
idx));
switch (map->cache_word_size) {
case 1: {
const u8 *cache = base;
return cache[idx];
}
case 2: {
const u16 *cache = base;
return cache[idx];
}
case 4: {
const u32 *cache = base;
return cache[idx];
}
default:
BUG();
}
/* unreachable */
return -1;
}
int regcache_lookup_reg(struct regmap *map, unsigned int reg)
{
struct reg_default key;
struct reg_default *r;
key.reg = reg;
key.def = 0;
r = bsearch(&key, map->reg_defaults, map->num_reg_defaults,
sizeof(struct reg_default), regcache_defaults_cmp);
if (r)
return r - map->reg_defaults;
else
return -ENOENT;
}
static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx)
{
if (!cache_present)
return true;
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,
unsigned int start, unsigned int end)
{
unsigned int i, regtmp, val;
int ret;
for (i = start; i < end; i++) {
regtmp = block_base + (i * map->reg_stride);
if (!regcache_reg_present(cache_present, i) ||
!regmap_writeable(map, regtmp))
continue;
val = regcache_get_val(map, block, i);
ret = regcache_sync_val(map, regtmp, val);
if (ret != 0)
return ret;
}
return 0;
}
static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,
unsigned int base, unsigned int cur)
{
size_t val_bytes = map->format.val_bytes;
int ret, count;
if (*data == NULL)
return 0;
count = (cur - base) / map->reg_stride;
dev_dbg(map->dev, "Writing %zu bytes for %d registers from 0x%x-0x%x\n",
count * val_bytes, count, base, cur - map->reg_stride);
map->cache_bypass = true;
ret = _regmap_raw_write(map, base, *data, count * val_bytes, false);
if (ret)
dev_err(map->dev, "Unable to sync registers %#x-%#x. %d\n",
base, cur - map->reg_stride, ret);
map->cache_bypass = false;
*data = NULL;
return ret;
}
static int regcache_sync_block_raw(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base, unsigned int start,
unsigned int end)
{
unsigned int regtmp = 0;
unsigned int base = 0;
const void *data = NULL;
unsigned int val;
int ret;
for (unsigned int i = start; i < end; i++) {
regtmp = block_base + (i * map->reg_stride);
if (!regcache_reg_present(cache_present, i) ||
!regmap_writeable(map, regtmp)) {
ret = regcache_sync_block_raw_flush(map, &data,
base, regtmp);
if (ret != 0)
return ret;
continue;
}
val = regcache_get_val(map, block, i);
if (!regcache_reg_needs_sync(map, regtmp, val)) {
ret = regcache_sync_block_raw_flush(map, &data,
base, regtmp);
if (ret != 0)
return ret;
continue;
}
if (!data) {
data = regcache_get_val_addr(map, block, i);
base = regtmp;
}
}
return regcache_sync_block_raw_flush(map, &data, base, regtmp +
map->reg_stride);
}
int regcache_sync_block(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base, unsigned int start,
unsigned int end)
{
if (regmap_can_raw_write(map) && !map->use_single_write)
return regcache_sync_block_raw(map, block, cache_present,
block_base, start, end);
else
return regcache_sync_block_single(map, block, cache_present,
block_base, start, end);
}
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register map access API - AC'97 support
//
// Copyright 2013 Linaro Ltd. All rights reserved.
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/ac97_codec.h>
bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case AC97_RESET:
case AC97_POWERDOWN:
case AC97_INT_PAGING:
case AC97_EXTENDED_ID:
case AC97_EXTENDED_STATUS:
case AC97_EXTENDED_MID:
case AC97_EXTENDED_MSTATUS:
case AC97_GPIO_STATUS:
case AC97_MISC_AFE:
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
case AC97_CODEC_CLASS_REV:
case AC97_PCI_SVID:
case AC97_PCI_SID:
case AC97_FUNC_SELECT:
case AC97_FUNC_INFO:
case AC97_SENSE_INFO:
return true;
default:
return false;
}
}
EXPORT_SYMBOL_GPL(regmap_ac97_default_volatile);
static int regmap_ac97_reg_read(void *context, unsigned int reg,
unsigned int *val)
{
struct snd_ac97 *ac97 = context;
*val = ac97->bus->ops->read(ac97, reg);
return 0;
}
static int regmap_ac97_reg_write(void *context, unsigned int reg,
unsigned int val)
{
struct snd_ac97 *ac97 = context;
ac97->bus->ops->write(ac97, reg, val);
return 0;
}
static const struct regmap_bus ac97_regmap_bus = {
.reg_write = regmap_ac97_reg_write,
.reg_read = regmap_ac97_reg_read,
};
struct regmap *__regmap_init_ac97(struct snd_ac97 *ac97,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
return __regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_ac97);
struct regmap *__devm_regmap_init_ac97(struct snd_ac97 *ac97,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
return __devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_ac97);
MODULE_DESCRIPTION("Register map access API - AC'97 support");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,704 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register map access API - debugfs
//
// Copyright 2011 Wolfson Microelectronics plc
//
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/idr.h>
#include "internal.h"
struct regmap_debugfs_node {
struct regmap *map;
struct list_head link;
};
static DEFINE_IDA(dummy_ida);
static struct dentry *regmap_debugfs_root;
static LIST_HEAD(regmap_debugfs_early_list);
static DEFINE_MUTEX(regmap_debugfs_early_lock);
/* Calculate the length of a fixed format */
static size_t regmap_calc_reg_len(int max_val)
{
return snprintf(NULL, 0, "%x", max_val);
}
static ssize_t regmap_name_read_file(struct file *file,
char __user *user_buf, size_t count,
loff_t *ppos)
{
struct regmap *map = file->private_data;
const char *name = "nodev";
int ret;
char *buf;
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (map->dev && map->dev->driver)
name = map->dev->driver->name;
ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
if (ret >= PAGE_SIZE) {
kfree(buf);
return ret;
}
ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
kfree(buf);
return ret;
}
static const struct file_operations regmap_name_fops = {
.open = simple_open,
.read = regmap_name_read_file,
.llseek = default_llseek,
};
static void regmap_debugfs_free_dump_cache(struct regmap *map)
{
struct regmap_debugfs_off_cache *c;
while (!list_empty(&map->debugfs_off_cache)) {
c = list_first_entry(&map->debugfs_off_cache,
struct regmap_debugfs_off_cache,
list);
list_del(&c->list);
kfree(c);
}
}
static bool regmap_printable(struct regmap *map, unsigned int reg)
{
if (regmap_precious(map, reg))
return false;
if (!regmap_readable(map, reg) && !regmap_cached(map, reg))
return false;
return true;
}
/*
* Work out where the start offset maps into register numbers, bearing
* in mind that we suppress hidden registers.
*/
static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
unsigned int base,
loff_t from,
loff_t *pos)
{
struct regmap_debugfs_off_cache *c = NULL;
loff_t p = 0;
unsigned int i, ret;
unsigned int fpos_offset;
unsigned int reg_offset;
/* Suppress the cache if we're using a subrange */
if (base)
return base;
/*
* If we don't have a cache build one so we don't have to do a
* linear scan each time.
*/
mutex_lock(&map->cache_lock);
i = base;
if (list_empty(&map->debugfs_off_cache)) {
for (; i <= map->max_register; i += map->reg_stride) {
/* Skip unprinted registers, closing off cache entry */
if (!regmap_printable(map, i)) {
if (c) {
c->max = p - 1;
c->max_reg = i - map->reg_stride;
list_add_tail(&c->list,
&map->debugfs_off_cache);
c = NULL;
}
continue;
}
/* No cache entry? Start a new one */
if (!c) {
c = kzalloc_obj(*c);
if (!c) {
regmap_debugfs_free_dump_cache(map);
mutex_unlock(&map->cache_lock);
return base;
}
c->min = p;
c->base_reg = i;
}
p += map->debugfs_tot_len;
}
}
/* Close the last entry off if we didn't scan beyond it */
if (c) {
c->max = p - 1;
c->max_reg = i - map->reg_stride;
list_add_tail(&c->list,
&map->debugfs_off_cache);
}
/*
* This should never happen; we return above if we fail to
* allocate and we should never be in this code if there are
* no registers at all.
*/
WARN_ON(list_empty(&map->debugfs_off_cache));
ret = base;
/* Find the relevant block:offset */
list_for_each_entry(c, &map->debugfs_off_cache, list) {
if (from >= c->min && from <= c->max) {
fpos_offset = from - c->min;
reg_offset = fpos_offset / map->debugfs_tot_len;
*pos = c->min + (reg_offset * map->debugfs_tot_len);
mutex_unlock(&map->cache_lock);
return c->base_reg + (reg_offset * map->reg_stride);
}
*pos = c->max;
ret = c->max_reg;
}
mutex_unlock(&map->cache_lock);
return ret;
}
static inline void regmap_calc_tot_len(struct regmap *map,
void *buf, size_t count)
{
/* Calculate the length of a fixed format */
if (!map->debugfs_tot_len) {
map->debugfs_reg_len = regmap_calc_reg_len(map->max_register);
map->debugfs_val_len = 2 * map->format.val_bytes;
map->debugfs_tot_len = map->debugfs_reg_len +
map->debugfs_val_len + 3; /* : \n */
}
}
static int regmap_next_readable_reg(struct regmap *map, int reg)
{
struct regmap_debugfs_off_cache *c;
int ret = -EINVAL;
if (regmap_printable(map, reg + map->reg_stride)) {
ret = reg + map->reg_stride;
} else {
mutex_lock(&map->cache_lock);
list_for_each_entry(c, &map->debugfs_off_cache, list) {
if (reg > c->max_reg)
continue;
if (reg < c->base_reg) {
ret = c->base_reg;
break;
}
}
mutex_unlock(&map->cache_lock);
}
return ret;
}
static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
unsigned int to, char __user *user_buf,
size_t count, loff_t *ppos)
{
size_t buf_pos = 0;
loff_t p = *ppos;
ssize_t ret;
int i;
char *buf;
unsigned int val, start_reg;
if (*ppos < 0 || !count)
return -EINVAL;
if (count > (PAGE_SIZE << MAX_PAGE_ORDER))
count = PAGE_SIZE << MAX_PAGE_ORDER;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
regmap_calc_tot_len(map, buf, count);
/* Work out which register we're starting at */
start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p);
for (i = start_reg; i >= 0 && i <= to;
i = regmap_next_readable_reg(map, i)) {
/* If we're in the region the user is trying to read */
if (p >= *ppos) {
/* ...but not beyond it */
if (buf_pos + map->debugfs_tot_len > count)
break;
/* Format the register */
snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
map->debugfs_reg_len, i - from);
buf_pos += map->debugfs_reg_len + 2;
/* Format the value, write all X if we can't read */
ret = regmap_read(map, i, &val);
if (ret == 0)
snprintf(buf + buf_pos, count - buf_pos,
"%.*x", map->debugfs_val_len, val);
else
memset(buf + buf_pos, 'X',
map->debugfs_val_len);
buf_pos += 2 * map->format.val_bytes;
buf[buf_pos++] = '\n';
}
p += map->debugfs_tot_len;
}
ret = buf_pos;
if (copy_to_user(user_buf, buf, buf_pos)) {
ret = -EFAULT;
goto out;
}
*ppos += buf_pos;
out:
kfree(buf);
return ret;
}
static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct regmap *map = file->private_data;
return regmap_read_debugfs(map, 0, map->max_register, user_buf,
count, ppos);
}
#undef REGMAP_ALLOW_WRITE_DEBUGFS
#ifdef REGMAP_ALLOW_WRITE_DEBUGFS
/*
* This can be dangerous especially when we have clients such as
* PMICs, therefore don't provide any real compile time configuration option
* for this feature, people who want to use this will need to modify
* the source code directly.
*/
static ssize_t regmap_map_write_file(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
char buf[32];
size_t buf_size;
char *start = buf;
unsigned long reg, value;
struct regmap *map = file->private_data;
int ret;
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
while (*start == ' ')
start++;
reg = simple_strtoul(start, &start, 16);
while (*start == ' ')
start++;
if (kstrtoul(start, 16, &value))
return -EINVAL;
/* Userspace has been fiddling around behind the kernel's back */
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
ret = regmap_write(map, reg, value);
if (ret < 0)
return ret;
return buf_size;
}
#else
#define regmap_map_write_file NULL
#endif
static const struct file_operations regmap_map_fops = {
.open = simple_open,
.read = regmap_map_read_file,
.write = regmap_map_write_file,
.llseek = default_llseek,
};
static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct regmap_range_node *range = file->private_data;
struct regmap *map = range->map;
return regmap_read_debugfs(map, range->range_min, range->range_max,
user_buf, count, ppos);
}
static const struct file_operations regmap_range_fops = {
.open = simple_open,
.read = regmap_range_read_file,
.llseek = default_llseek,
};
static ssize_t regmap_reg_ranges_read_file(struct file *file,
char __user *user_buf, size_t count,
loff_t *ppos)
{
struct regmap *map = file->private_data;
struct regmap_debugfs_off_cache *c;
loff_t p = 0;
size_t buf_pos = 0;
char *buf;
char *entry;
int ret;
unsigned int entry_len;
if (*ppos < 0 || !count)
return -EINVAL;
if (count > (PAGE_SIZE << MAX_PAGE_ORDER))
count = PAGE_SIZE << MAX_PAGE_ORDER;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
entry = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!entry) {
kfree(buf);
return -ENOMEM;
}
/* While we are at it, build the register dump cache
* now so the read() operation on the `registers' file
* can benefit from using the cache. We do not care
* about the file position information that is contained
* in the cache, just about the actual register blocks */
regmap_calc_tot_len(map, buf, count);
regmap_debugfs_get_dump_start(map, 0, *ppos, &p);
/* Reset file pointer as the fixed-format of the `registers'
* file is not compatible with the `range' file */
p = 0;
mutex_lock(&map->cache_lock);
list_for_each_entry(c, &map->debugfs_off_cache, list) {
entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n",
c->base_reg, c->max_reg);
if (p >= *ppos) {
if (buf_pos + entry_len > count)
break;
memcpy(buf + buf_pos, entry, entry_len);
buf_pos += entry_len;
}
p += entry_len;
}
mutex_unlock(&map->cache_lock);
kfree(entry);
ret = buf_pos;
if (copy_to_user(user_buf, buf, buf_pos)) {
ret = -EFAULT;
goto out_buf;
}
*ppos += buf_pos;
out_buf:
kfree(buf);
return ret;
}
static const struct file_operations regmap_reg_ranges_fops = {
.open = simple_open,
.read = regmap_reg_ranges_read_file,
.llseek = default_llseek,
};
static int regmap_access_show(struct seq_file *s, void *ignored)
{
struct regmap *map = s->private;
int i, reg_len;
reg_len = regmap_calc_reg_len(map->max_register);
for (i = 0; i <= map->max_register; i += map->reg_stride) {
/* Ignore registers which are neither readable nor writable */
if (!regmap_readable(map, i) && !regmap_writeable(map, i))
continue;
/* Format the register */
seq_printf(s, "%.*x: %c %c %c %c\n", reg_len, i,
regmap_readable(map, i) ? 'y' : 'n',
regmap_writeable(map, i) ? 'y' : 'n',
regmap_volatile(map, i) ? 'y' : 'n',
regmap_precious(map, i) ? 'y' : 'n');
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(regmap_access);
static ssize_t regmap_cache_only_write_file(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct regmap *map = container_of(file->private_data,
struct regmap, cache_only);
bool new_val, require_sync = false;
int err;
err = kstrtobool_from_user(user_buf, count, &new_val);
/* Ignore malforned data like debugfs_write_file_bool() */
if (err)
return count;
map->lock(map->lock_arg);
if (new_val && !map->cache_only) {
dev_warn(map->dev, "debugfs cache_only=Y forced\n");
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
} else if (!new_val && map->cache_only) {
dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n");
require_sync = true;
}
map->cache_only = new_val;
map->unlock(map->lock_arg);
if (require_sync) {
err = regcache_sync(map);
if (err)
dev_err(map->dev, "Failed to sync cache %d\n", err);
}
return count;
}
static const struct file_operations regmap_cache_only_fops = {
.open = simple_open,
.read = debugfs_read_file_bool,
.write = regmap_cache_only_write_file,
};
static ssize_t regmap_cache_bypass_write_file(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct regmap *map = container_of(file->private_data,
struct regmap, cache_bypass);
bool new_val;
int err;
err = kstrtobool_from_user(user_buf, count, &new_val);
/* Ignore malforned data like debugfs_write_file_bool() */
if (err)
return count;
map->lock(map->lock_arg);
if (new_val && !map->cache_bypass) {
dev_warn(map->dev, "debugfs cache_bypass=Y forced\n");
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
} else if (!new_val && map->cache_bypass) {
dev_warn(map->dev, "debugfs cache_bypass=N forced\n");
}
map->cache_bypass = new_val;
map->unlock(map->lock_arg);
return count;
}
static const struct file_operations regmap_cache_bypass_fops = {
.open = simple_open,
.read = debugfs_read_file_bool,
.write = regmap_cache_bypass_write_file,
};
void regmap_debugfs_init(struct regmap *map)
{
struct rb_node *next;
struct regmap_range_node *range_node;
const char *devname = "dummy";
const char *name = map->name;
int id;
/*
* Userspace can initiate reads from the hardware over debugfs.
* Normally internal regmap structures and buffers are protected with
* a mutex or a spinlock, but if the regmap owner decided to disable
* all locking mechanisms, this is no longer the case. For safety:
* don't create the debugfs entries if locking is disabled.
*/
if (map->debugfs_disable) {
dev_dbg(map->dev, "regmap locking disabled - not creating debugfs entries\n");
return;
}
/* If we don't have the debugfs root yet, postpone init */
if (!regmap_debugfs_root) {
struct regmap_debugfs_node *node;
node = kzalloc_obj(*node);
if (!node)
return;
node->map = map;
mutex_lock(&regmap_debugfs_early_lock);
list_add(&node->link, &regmap_debugfs_early_list);
mutex_unlock(&regmap_debugfs_early_lock);
return;
}
INIT_LIST_HEAD(&map->debugfs_off_cache);
mutex_init(&map->cache_lock);
map->debugfs_dummy_id = -1;
if (map->dev)
devname = dev_name(map->dev);
if (name) {
if (!map->debugfs_name) {
map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
devname, name);
if (!map->debugfs_name)
return;
}
name = map->debugfs_name;
} else {
name = devname;
}
if (!strcmp(name, "dummy")) {
kfree(map->debugfs_name);
id = ida_alloc(&dummy_ida, GFP_KERNEL);
if (id < 0)
return;
map->debugfs_name = kasprintf(GFP_KERNEL, "dummy%d", id);
if (!map->debugfs_name) {
ida_free(&dummy_ida, id);
return;
}
map->debugfs_dummy_id = id;
name = map->debugfs_name;
}
map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
debugfs_create_file("name", 0400, map->debugfs,
map, &regmap_name_fops);
debugfs_create_file("range", 0400, map->debugfs,
map, &regmap_reg_ranges_fops);
if (map->max_register || regmap_readable(map, 0)) {
umode_t registers_mode;
#if defined(REGMAP_ALLOW_WRITE_DEBUGFS)
registers_mode = 0600;
#else
registers_mode = 0400;
#endif
debugfs_create_file("registers", registers_mode, map->debugfs,
map, &regmap_map_fops);
debugfs_create_file("access", 0400, map->debugfs,
map, &regmap_access_fops);
}
if (map->cache_type) {
debugfs_create_file("cache_only", 0600, map->debugfs,
&map->cache_only, &regmap_cache_only_fops);
debugfs_create_bool("cache_dirty", 0400, map->debugfs,
&map->cache_dirty);
debugfs_create_file("cache_bypass", 0600, map->debugfs,
&map->cache_bypass,
&regmap_cache_bypass_fops);
}
/*
* This could interfere with driver operation. Therefore, don't provide
* any real compile time configuration option for this feature. One will
* have to modify the source code directly in order to use it.
*/
#undef REGMAP_ALLOW_FORCE_WRITE_FIELD_DEBUGFS
#ifdef REGMAP_ALLOW_FORCE_WRITE_FIELD_DEBUGFS
debugfs_create_bool("force_write_field", 0600, map->debugfs,
&map->force_write_field);
#endif
next = rb_first(&map->range_tree);
while (next) {
range_node = rb_entry(next, struct regmap_range_node, node);
if (range_node->name)
debugfs_create_file(range_node->name, 0400,
map->debugfs, range_node,
&regmap_range_fops);
next = rb_next(&range_node->node);
}
if (map->cache_ops && map->cache_ops->debugfs_init)
map->cache_ops->debugfs_init(map);
}
void regmap_debugfs_exit(struct regmap *map)
{
if (map->debugfs) {
debugfs_remove_recursive(map->debugfs);
mutex_lock(&map->cache_lock);
regmap_debugfs_free_dump_cache(map);
mutex_unlock(&map->cache_lock);
if (map->debugfs_dummy_id >= 0) {
ida_free(&dummy_ida, map->debugfs_dummy_id);
map->debugfs_dummy_id = -1;
}
kfree(map->debugfs_name);
map->debugfs_name = NULL;
} else {
struct regmap_debugfs_node *node, *tmp;
mutex_lock(&regmap_debugfs_early_lock);
list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list,
link) {
if (node->map == map) {
list_del(&node->link);
kfree(node);
}
}
mutex_unlock(&regmap_debugfs_early_lock);
}
}
void regmap_debugfs_initcall(void)
{
struct regmap_debugfs_node *node, *tmp;
regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
mutex_lock(&regmap_debugfs_early_lock);
list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) {
regmap_debugfs_init(node->map);
list_del(&node->link);
kfree(node);
}
mutex_unlock(&regmap_debugfs_early_lock);
}
@@ -0,0 +1,231 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register map access API - FSI support
//
// Copyright 2022 IBM Corp
//
// Author: Eddie James <eajames@linux.ibm.com>
#include <linux/fsi.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "internal.h"
static int regmap_fsi32_reg_read(void *context, unsigned int reg, unsigned int *val)
{
u32 v;
int ret;
ret = fsi_slave_read(context, reg, &v, sizeof(v));
if (ret)
return ret;
*val = v;
return 0;
}
static int regmap_fsi32_reg_write(void *context, unsigned int reg, unsigned int val)
{
u32 v = val;
return fsi_slave_write(context, reg, &v, sizeof(v));
}
static const struct regmap_bus regmap_fsi32 = {
.reg_write = regmap_fsi32_reg_write,
.reg_read = regmap_fsi32_reg_read,
};
static int regmap_fsi32le_reg_read(void *context, unsigned int reg, unsigned int *val)
{
__be32 v;
int ret;
ret = fsi_slave_read(context, reg, &v, sizeof(v));
if (ret)
return ret;
*val = be32_to_cpu(v);
return 0;
}
static int regmap_fsi32le_reg_write(void *context, unsigned int reg, unsigned int val)
{
__be32 v = cpu_to_be32(val);
return fsi_slave_write(context, reg, &v, sizeof(v));
}
static const struct regmap_bus regmap_fsi32le = {
.reg_write = regmap_fsi32le_reg_write,
.reg_read = regmap_fsi32le_reg_read,
};
static int regmap_fsi16_reg_read(void *context, unsigned int reg, unsigned int *val)
{
u16 v;
int ret;
ret = fsi_slave_read(context, reg, &v, sizeof(v));
if (ret)
return ret;
*val = v;
return 0;
}
static int regmap_fsi16_reg_write(void *context, unsigned int reg, unsigned int val)
{
u16 v;
if (val > 0xffff)
return -EINVAL;
v = val;
return fsi_slave_write(context, reg, &v, sizeof(v));
}
static const struct regmap_bus regmap_fsi16 = {
.reg_write = regmap_fsi16_reg_write,
.reg_read = regmap_fsi16_reg_read,
};
static int regmap_fsi16le_reg_read(void *context, unsigned int reg, unsigned int *val)
{
__be16 v;
int ret;
ret = fsi_slave_read(context, reg, &v, sizeof(v));
if (ret)
return ret;
*val = be16_to_cpu(v);
return 0;
}
static int regmap_fsi16le_reg_write(void *context, unsigned int reg, unsigned int val)
{
__be16 v;
if (val > 0xffff)
return -EINVAL;
v = cpu_to_be16(val);
return fsi_slave_write(context, reg, &v, sizeof(v));
}
static const struct regmap_bus regmap_fsi16le = {
.reg_write = regmap_fsi16le_reg_write,
.reg_read = regmap_fsi16le_reg_read,
};
static int regmap_fsi8_reg_read(void *context, unsigned int reg, unsigned int *val)
{
u8 v;
int ret;
ret = fsi_slave_read(context, reg, &v, sizeof(v));
if (ret)
return ret;
*val = v;
return 0;
}
static int regmap_fsi8_reg_write(void *context, unsigned int reg, unsigned int val)
{
u8 v;
if (val > 0xff)
return -EINVAL;
v = val;
return fsi_slave_write(context, reg, &v, sizeof(v));
}
static const struct regmap_bus regmap_fsi8 = {
.reg_write = regmap_fsi8_reg_write,
.reg_read = regmap_fsi8_reg_read,
};
static const struct regmap_bus *regmap_get_fsi_bus(struct fsi_device *fsi_dev,
const struct regmap_config *config)
{
const struct regmap_bus *bus = NULL;
if (config->reg_bits == 8 || config->reg_bits == 16 || config->reg_bits == 32) {
switch (config->val_bits) {
case 8:
bus = &regmap_fsi8;
break;
case 16:
switch (regmap_get_val_endian(&fsi_dev->dev, NULL, config)) {
case REGMAP_ENDIAN_LITTLE:
#ifdef __LITTLE_ENDIAN
case REGMAP_ENDIAN_NATIVE:
#endif
bus = &regmap_fsi16le;
break;
case REGMAP_ENDIAN_DEFAULT:
case REGMAP_ENDIAN_BIG:
#ifdef __BIG_ENDIAN
case REGMAP_ENDIAN_NATIVE:
#endif
bus = &regmap_fsi16;
break;
default:
break;
}
break;
case 32:
switch (regmap_get_val_endian(&fsi_dev->dev, NULL, config)) {
case REGMAP_ENDIAN_LITTLE:
#ifdef __LITTLE_ENDIAN
case REGMAP_ENDIAN_NATIVE:
#endif
bus = &regmap_fsi32le;
break;
case REGMAP_ENDIAN_DEFAULT:
case REGMAP_ENDIAN_BIG:
#ifdef __BIG_ENDIAN
case REGMAP_ENDIAN_NATIVE:
#endif
bus = &regmap_fsi32;
break;
default:
break;
}
break;
}
}
return bus ?: ERR_PTR(-EOPNOTSUPP);
}
struct regmap *__regmap_init_fsi(struct fsi_device *fsi_dev, const struct regmap_config *config,
struct lock_class_key *lock_key, const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_fsi_bus(fsi_dev, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __regmap_init(&fsi_dev->dev, bus, fsi_dev->slave, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_fsi);
struct regmap *__devm_regmap_init_fsi(struct fsi_device *fsi_dev,
const struct regmap_config *config,
struct lock_class_key *lock_key, const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_fsi_bus(fsi_dev, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __devm_regmap_init(&fsi_dev->dev, bus, fsi_dev->slave, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_fsi);
MODULE_LICENSE("GPL");
@@ -0,0 +1,450 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register map access API - I2C support
//
// Copyright 2011 Wolfson Microelectronics plc
//
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
#include <linux/regmap.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include "internal.h"
static int regmap_smbus_byte_reg_read(void *context, unsigned int reg,
unsigned int *val)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
int ret;
if (reg > 0xff)
return -EINVAL;
ret = i2c_smbus_read_byte_data(i2c, reg);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
static int regmap_smbus_byte_reg_write(void *context, unsigned int reg,
unsigned int val)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
if (val > 0xff || reg > 0xff)
return -EINVAL;
return i2c_smbus_write_byte_data(i2c, reg, val);
}
static const struct regmap_bus regmap_smbus_byte = {
.reg_write = regmap_smbus_byte_reg_write,
.reg_read = regmap_smbus_byte_reg_read,
};
static int regmap_smbus_word_reg_read(void *context, unsigned int reg,
unsigned int *val)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
int ret;
if (reg > 0xff)
return -EINVAL;
ret = i2c_smbus_read_word_data(i2c, reg);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
static int regmap_smbus_word_reg_write(void *context, unsigned int reg,
unsigned int val)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
if (val > 0xffff || reg > 0xff)
return -EINVAL;
return i2c_smbus_write_word_data(i2c, reg, val);
}
static const struct regmap_bus regmap_smbus_word = {
.reg_write = regmap_smbus_word_reg_write,
.reg_read = regmap_smbus_word_reg_read,
};
static int regmap_smbus_word_read_swapped(void *context, unsigned int reg,
unsigned int *val)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
int ret;
if (reg > 0xff)
return -EINVAL;
ret = i2c_smbus_read_word_swapped(i2c, reg);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
static int regmap_smbus_word_write_swapped(void *context, unsigned int reg,
unsigned int val)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
if (val > 0xffff || reg > 0xff)
return -EINVAL;
return i2c_smbus_write_word_swapped(i2c, reg, val);
}
static const struct regmap_bus regmap_smbus_word_swapped = {
.reg_write = regmap_smbus_word_write_swapped,
.reg_read = regmap_smbus_word_read_swapped,
};
static int regmap_i2c_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
int ret;
ret = i2c_master_send(i2c, data, count);
if (ret == count)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static int regmap_i2c_gather_write(void *context,
const void *reg, size_t reg_size,
const void *val, size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct i2c_msg xfer[2];
int ret;
/* If the I2C controller can't do a gather tell the core, it
* will substitute in a linear write for us.
*/
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_NOSTART))
return -ENOTSUPP;
xfer[0].addr = i2c->addr;
xfer[0].flags = 0;
xfer[0].len = reg_size;
xfer[0].buf = (void *)reg;
xfer[1].addr = i2c->addr;
xfer[1].flags = I2C_M_NOSTART;
xfer[1].len = val_size;
xfer[1].buf = (void *)val;
ret = i2c_transfer(i2c->adapter, xfer, 2);
if (ret == 2)
return 0;
if (ret < 0)
return ret;
else
return -EIO;
}
static int regmap_i2c_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct i2c_msg xfer[2];
int ret;
xfer[0].addr = i2c->addr;
xfer[0].flags = 0;
xfer[0].len = reg_size;
xfer[0].buf = (void *)reg;
xfer[1].addr = i2c->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = val_size;
xfer[1].buf = val;
ret = i2c_transfer(i2c->adapter, xfer, 2);
if (ret == 2)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static const struct regmap_bus regmap_i2c = {
.write = regmap_i2c_write,
.gather_write = regmap_i2c_gather_write,
.read = regmap_i2c_read,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
static int regmap_i2c_smbus_i2c_write(void *context, const void *data,
size_t count)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
if (count < 1)
return -EINVAL;
--count;
return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
((u8 *)data + 1));
}
static int regmap_i2c_smbus_i2c_read(void *context, const void *reg,
size_t reg_size, void *val,
size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
int ret;
if (reg_size != 1 || val_size < 1)
return -EINVAL;
ret = i2c_smbus_read_i2c_block_data(i2c, ((u8 *)reg)[0], val_size, val);
if (ret == val_size)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static const struct regmap_bus regmap_i2c_smbus_i2c_block = {
.write = regmap_i2c_smbus_i2c_write,
.read = regmap_i2c_smbus_i2c_read,
.max_raw_read = I2C_SMBUS_BLOCK_MAX - 1,
.max_raw_write = I2C_SMBUS_BLOCK_MAX - 1,
};
static int regmap_i2c_smbus_i2c_write_reg16(void *context, const void *data,
size_t count)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
if (count < 2)
return -EINVAL;
count--;
return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
(u8 *)data + 1);
}
static int regmap_i2c_smbus_i2c_read_reg16(void *context, const void *reg,
size_t reg_size, void *val,
size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
int ret, count, len = val_size;
if (reg_size != 2)
return -EINVAL;
ret = i2c_smbus_write_byte_data(i2c, ((u16 *)reg)[0] & 0xff,
((u16 *)reg)[0] >> 8);
if (ret < 0)
return ret;
count = 0;
do {
/* Current Address Read */
ret = i2c_smbus_read_byte(i2c);
if (ret < 0)
break;
*((u8 *)val++) = ret;
count++;
len--;
} while (len > 0);
if (count == val_size)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = {
.write = regmap_i2c_smbus_i2c_write_reg16,
.read = regmap_i2c_smbus_i2c_read_reg16,
.max_raw_read = I2C_SMBUS_BLOCK_MAX - 2,
.max_raw_write = I2C_SMBUS_BLOCK_MAX - 2,
};
/*
* SMBus byte/word reg16 support for adapters that have SMBUS_BYTE_DATA
* and SMBUS_WORD_DATA but lack I2C_FUNC_I2C and I2C_FUNC_SMBUS_I2C_BLOCK,
* such as the AMD PIIX4.
*
* READ: set 16-bit EEPROM address via write_byte_data(addr_lo, addr_hi),
* then sequentially read bytes via read_byte() (EEPROM auto-
* increments the address pointer). Same as the I2C-block reg16
* read path above.
*
* WRITE: encode the low address byte and data into a word transaction:
* write_word_data(addr_hi, (data_byte << 8) | addr_lo).
* Only single-byte writes are supported (one value per transaction).
*/
static int regmap_smbus_word_write_reg16(void *context, const void *data,
size_t count)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
u8 addr_hi, addr_lo, val;
/*
* data layout: [addr_hi, addr_lo, val0, val1, ...].
* Only single-byte value writes are supported; multi-byte would
* require raw I2C (or repeated word writes with incrementing address).
*/
if (count != 3)
return -EINVAL;
addr_hi = ((u8 *)data)[0];
addr_lo = ((u8 *)data)[1];
val = ((u8 *)data)[2];
return i2c_smbus_write_word_data(i2c, addr_hi,
((u16)val << 8) | addr_lo);
}
static const struct regmap_bus regmap_smbus_byte_word_reg16 = {
.write = regmap_smbus_word_write_reg16,
.read = regmap_i2c_smbus_i2c_read_reg16,
.max_raw_read = I2C_SMBUS_BLOCK_MAX - 2,
.max_raw_write = 1,
};
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
const struct regmap_config *config)
{
const struct i2c_adapter_quirks *quirks;
const struct regmap_bus *bus = NULL;
struct regmap_bus *ret_bus;
u16 max_read = 0, max_write = 0;
if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
bus = &regmap_i2c;
else if (config->val_bits == 8 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
bus = &regmap_i2c_smbus_i2c_block;
else if (config->val_bits == 8 && config->reg_bits == 16 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
bus = &regmap_i2c_smbus_i2c_block_reg16;
else if (config->val_bits == 8 && config->reg_bits == 16 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
bus = &regmap_smbus_byte_word_reg16;
else if (config->val_bits == 16 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_WORD_DATA))
switch (regmap_get_val_endian(&i2c->dev, NULL, config)) {
case REGMAP_ENDIAN_LITTLE:
bus = &regmap_smbus_word;
break;
case REGMAP_ENDIAN_BIG:
bus = &regmap_smbus_word_swapped;
break;
default: /* everything else is not supported */
break;
}
else if (config->val_bits == 8 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_BYTE_DATA))
bus = &regmap_smbus_byte;
if (!bus)
return ERR_PTR(-ENOTSUPP);
quirks = i2c->adapter->quirks;
if (quirks) {
if (quirks->max_read_len &&
(bus->max_raw_read == 0 || bus->max_raw_read > quirks->max_read_len))
max_read = quirks->max_read_len;
if (quirks->max_write_len &&
(bus->max_raw_write == 0 || bus->max_raw_write > quirks->max_write_len))
max_write = quirks->max_write_len -
(config->reg_bits + config->pad_bits) / BITS_PER_BYTE;
if (max_read || max_write) {
ret_bus = kmemdup(bus, sizeof(*bus), GFP_KERNEL);
if (!ret_bus)
return ERR_PTR(-ENOMEM);
ret_bus->free_on_exit = true;
ret_bus->max_raw_read = max_read;
ret_bus->max_raw_write = max_write;
bus = ret_bus;
}
}
return bus;
}
struct regmap *__regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __regmap_init(&i2c->dev, bus, &i2c->dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_i2c);
struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __devm_regmap_init(&i2c->dev, bus, &i2c->dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_i2c);
MODULE_DESCRIPTION("Register map access API - I2C support");
MODULE_LICENSE("GPL");
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
#include <linux/array_size.h>
#include <linux/regmap.h>
#include <linux/i3c/device.h>
#include <linux/i3c/master.h>
#include <linux/module.h>
static int regmap_i3c_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct i3c_device *i3c = dev_to_i3cdev(dev);
struct i3c_xfer xfers[] = {
{
.rnw = false,
.len = count,
.data.out = data,
},
};
return i3c_device_do_xfers(i3c, xfers, ARRAY_SIZE(xfers), I3C_SDR);
}
static int regmap_i3c_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct device *dev = context;
struct i3c_device *i3c = dev_to_i3cdev(dev);
struct i3c_xfer xfers[2];
xfers[0].rnw = false;
xfers[0].len = reg_size;
xfers[0].data.out = reg;
xfers[1].rnw = true;
xfers[1].len = val_size;
xfers[1].data.in = val;
return i3c_device_do_xfers(i3c, xfers, ARRAY_SIZE(xfers), I3C_SDR);
}
static const struct regmap_bus regmap_i3c = {
.write = regmap_i3c_write,
.read = regmap_i3c_read,
};
struct regmap *__regmap_init_i3c(struct i3c_device *i3c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
return __regmap_init(&i3c->dev, &regmap_i3c, &i3c->dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_i3c);
struct regmap *__devm_regmap_init_i3c(struct i3c_device *i3c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
return __devm_regmap_init(&i3c->dev, &regmap_i3c, &i3c->dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_i3c);
MODULE_AUTHOR("Vitor Soares <vitor.soares@synopsys.com>");
MODULE_DESCRIPTION("regmap I3C Module");
MODULE_LICENSE("GPL v2");
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/errno.h>
#include <linux/mdio.h>
#include <linux/module.h>
#include <linux/regmap.h>
#define REGVAL_MASK GENMASK(15, 0)
#define REGNUM_C22_MASK GENMASK(4, 0)
/* Clause-45 mask includes the device type (5 bit) and actual register number (16 bit) */
#define REGNUM_C45_MASK GENMASK(20, 0)
static int regmap_mdio_c22_read(void *context, unsigned int reg, unsigned int *val)
{
struct mdio_device *mdio_dev = context;
int ret;
if (unlikely(reg & ~REGNUM_C22_MASK))
return -ENXIO;
ret = mdiodev_read(mdio_dev, reg);
if (ret < 0)
return ret;
*val = ret & REGVAL_MASK;
return 0;
}
static int regmap_mdio_c22_write(void *context, unsigned int reg, unsigned int val)
{
struct mdio_device *mdio_dev = context;
if (unlikely(reg & ~REGNUM_C22_MASK))
return -ENXIO;
return mdiodev_write(mdio_dev, reg, val);
}
static const struct regmap_bus regmap_mdio_c22_bus = {
.reg_write = regmap_mdio_c22_write,
.reg_read = regmap_mdio_c22_read,
};
static int regmap_mdio_c45_read(void *context, unsigned int reg, unsigned int *val)
{
struct mdio_device *mdio_dev = context;
unsigned int devad;
int ret;
if (unlikely(reg & ~REGNUM_C45_MASK))
return -ENXIO;
devad = reg >> REGMAP_MDIO_C45_DEVAD_SHIFT;
reg = reg & REGMAP_MDIO_C45_REGNUM_MASK;
ret = mdiodev_c45_read(mdio_dev, devad, reg);
if (ret < 0)
return ret;
*val = ret & REGVAL_MASK;
return 0;
}
static int regmap_mdio_c45_write(void *context, unsigned int reg, unsigned int val)
{
struct mdio_device *mdio_dev = context;
unsigned int devad;
if (unlikely(reg & ~REGNUM_C45_MASK))
return -ENXIO;
devad = reg >> REGMAP_MDIO_C45_DEVAD_SHIFT;
reg = reg & REGMAP_MDIO_C45_REGNUM_MASK;
return mdiodev_c45_write(mdio_dev, devad, reg, val);
}
static const struct regmap_bus regmap_mdio_c45_bus = {
.reg_write = regmap_mdio_c45_write,
.reg_read = regmap_mdio_c45_read,
};
struct regmap *__regmap_init_mdio(struct mdio_device *mdio_dev,
const struct regmap_config *config, struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus;
if (config->reg_bits == 5 && config->val_bits == 16)
bus = &regmap_mdio_c22_bus;
else if (config->reg_bits == 21 && config->val_bits == 16)
bus = &regmap_mdio_c45_bus;
else
return ERR_PTR(-EOPNOTSUPP);
return __regmap_init(&mdio_dev->dev, bus, mdio_dev, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_mdio);
struct regmap *__devm_regmap_init_mdio(struct mdio_device *mdio_dev,
const struct regmap_config *config, struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus;
if (config->reg_bits == 5 && config->val_bits == 16)
bus = &regmap_mdio_c22_bus;
else if (config->reg_bits == 21 && config->val_bits == 16)
bus = &regmap_mdio_c45_bus;
else
return ERR_PTR(-EOPNOTSUPP);
return __devm_regmap_init(&mdio_dev->dev, bus, mdio_dev, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_mdio);
MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
MODULE_DESCRIPTION("regmap MDIO Module");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,613 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register map access API - MMIO support
//
// Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
#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"
struct regmap_mmio_context {
void __iomem *regs;
unsigned int val_bytes;
bool big_endian;
bool attached_clk;
struct clk *clk;
void (*reg_write)(struct regmap_mmio_context *ctx,
unsigned int reg, unsigned int val);
unsigned int (*reg_read)(struct regmap_mmio_context *ctx,
unsigned int reg);
};
static int regmap_mmio_regbits_check(size_t reg_bits)
{
switch (reg_bits) {
case 8:
case 16:
case 32:
return 0;
default:
return -EINVAL;
}
}
static int regmap_mmio_get_min_stride(size_t val_bits)
{
int min_stride;
switch (val_bits) {
case 8:
/* The core treats 0 as 1 */
min_stride = 0;
break;
case 16:
min_stride = 2;
break;
case 32:
min_stride = 4;
break;
default:
return -EINVAL;
}
return min_stride;
}
static void regmap_mmio_write8(struct regmap_mmio_context *ctx,
unsigned int reg,
unsigned int val)
{
writeb(val, ctx->regs + reg);
}
static void regmap_mmio_write8_relaxed(struct regmap_mmio_context *ctx,
unsigned int reg,
unsigned int val)
{
writeb_relaxed(val, ctx->regs + reg);
}
static void regmap_mmio_iowrite8(struct regmap_mmio_context *ctx,
unsigned int reg, unsigned int val)
{
iowrite8(val, ctx->regs + reg);
}
static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
unsigned int reg,
unsigned int val)
{
writew(val, ctx->regs + reg);
}
static void regmap_mmio_write16le_relaxed(struct regmap_mmio_context *ctx,
unsigned int reg,
unsigned int val)
{
writew_relaxed(val, ctx->regs + reg);
}
static void regmap_mmio_iowrite16le(struct regmap_mmio_context *ctx,
unsigned int reg, unsigned int val)
{
iowrite16(val, ctx->regs + reg);
}
static void regmap_mmio_write16be(struct regmap_mmio_context *ctx,
unsigned int reg,
unsigned int val)
{
writew(swab16(val), ctx->regs + reg);
}
static void regmap_mmio_iowrite16be(struct regmap_mmio_context *ctx,
unsigned int reg, unsigned int val)
{
iowrite16be(val, ctx->regs + reg);
}
static void regmap_mmio_write32le(struct regmap_mmio_context *ctx,
unsigned int reg,
unsigned int val)
{
writel(val, ctx->regs + reg);
}
static void regmap_mmio_write32le_relaxed(struct regmap_mmio_context *ctx,
unsigned int reg,
unsigned int val)
{
writel_relaxed(val, ctx->regs + reg);
}
static void regmap_mmio_iowrite32le(struct regmap_mmio_context *ctx,
unsigned int reg, unsigned int val)
{
iowrite32(val, ctx->regs + reg);
}
static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
unsigned int reg,
unsigned int val)
{
writel(swab32(val), ctx->regs + reg);
}
static void regmap_mmio_iowrite32be(struct regmap_mmio_context *ctx,
unsigned int reg, unsigned int val)
{
iowrite32be(val, ctx->regs + reg);
}
static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
{
struct regmap_mmio_context *ctx = context;
int ret;
if (!IS_ERR(ctx->clk)) {
ret = clk_enable(ctx->clk);
if (ret < 0)
return ret;
}
ctx->reg_write(ctx, reg, val);
if (!IS_ERR(ctx->clk))
clk_disable(ctx->clk);
return 0;
}
static int regmap_mmio_noinc_write(void *context, unsigned int reg,
const void *val, size_t val_count)
{
struct regmap_mmio_context *ctx = context;
int ret = 0;
int i;
if (!IS_ERR(ctx->clk)) {
ret = clk_enable(ctx->clk);
if (ret < 0)
return ret;
}
/*
* There are no native, assembly-optimized write single register
* operations for big endian, so fall back to emulation if this
* is needed. (Single bytes are fine, they are not affected by
* endianness.)
*/
if (ctx->big_endian && (ctx->val_bytes > 1)) {
switch (ctx->val_bytes) {
case 2:
{
const u16 *valp = (const u16 *)val;
for (i = 0; i < val_count; i++)
writew(swab16(valp[i]), ctx->regs + reg);
goto out_clk;
}
case 4:
{
const u32 *valp = (const u32 *)val;
for (i = 0; i < val_count; i++)
writel(swab32(valp[i]), ctx->regs + reg);
goto out_clk;
}
default:
ret = -EINVAL;
goto out_clk;
}
}
switch (ctx->val_bytes) {
case 1:
writesb(ctx->regs + reg, (const u8 *)val, val_count);
break;
case 2:
writesw(ctx->regs + reg, (const u16 *)val, val_count);
break;
case 4:
writesl(ctx->regs + reg, (const u32 *)val, val_count);
break;
default:
ret = -EINVAL;
break;
}
out_clk:
if (!IS_ERR(ctx->clk))
clk_disable(ctx->clk);
return ret;
}
static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return readb(ctx->regs + reg);
}
static unsigned int regmap_mmio_read8_relaxed(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return readb_relaxed(ctx->regs + reg);
}
static unsigned int regmap_mmio_ioread8(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return ioread8(ctx->regs + reg);
}
static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return readw(ctx->regs + reg);
}
static unsigned int regmap_mmio_read16le_relaxed(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return readw_relaxed(ctx->regs + reg);
}
static unsigned int regmap_mmio_ioread16le(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return ioread16(ctx->regs + reg);
}
static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return swab16(readw(ctx->regs + reg));
}
static unsigned int regmap_mmio_ioread16be(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return ioread16be(ctx->regs + reg);
}
static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return readl(ctx->regs + reg);
}
static unsigned int regmap_mmio_read32le_relaxed(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return readl_relaxed(ctx->regs + reg);
}
static unsigned int regmap_mmio_ioread32le(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return ioread32(ctx->regs + reg);
}
static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return swab32(readl(ctx->regs + reg));
}
static unsigned int regmap_mmio_ioread32be(struct regmap_mmio_context *ctx,
unsigned int reg)
{
return ioread32be(ctx->regs + reg);
}
static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
{
struct regmap_mmio_context *ctx = context;
int ret;
if (!IS_ERR(ctx->clk)) {
ret = clk_enable(ctx->clk);
if (ret < 0)
return ret;
}
*val = ctx->reg_read(ctx, reg);
if (!IS_ERR(ctx->clk))
clk_disable(ctx->clk);
return 0;
}
static int regmap_mmio_noinc_read(void *context, unsigned int reg,
void *val, size_t val_count)
{
struct regmap_mmio_context *ctx = context;
int ret = 0;
if (!IS_ERR(ctx->clk)) {
ret = clk_enable(ctx->clk);
if (ret < 0)
return ret;
}
switch (ctx->val_bytes) {
case 1:
readsb(ctx->regs + reg, (u8 *)val, val_count);
break;
case 2:
readsw(ctx->regs + reg, (u16 *)val, val_count);
break;
case 4:
readsl(ctx->regs + reg, (u32 *)val, val_count);
break;
default:
ret = -EINVAL;
goto out_clk;
}
/*
* There are no native, assembly-optimized write single register
* operations for big endian, so fall back to emulation if this
* is needed. (Single bytes are fine, they are not affected by
* endianness.)
*/
if (ctx->big_endian && (ctx->val_bytes > 1)) {
switch (ctx->val_bytes) {
case 2:
swab16_array(val, val_count);
break;
case 4:
swab32_array(val, val_count);
break;
default:
ret = -EINVAL;
break;
}
}
out_clk:
if (!IS_ERR(ctx->clk))
clk_disable(ctx->clk);
return ret;
}
static void regmap_mmio_free_context(void *context)
{
struct regmap_mmio_context *ctx = context;
if (!IS_ERR(ctx->clk)) {
clk_unprepare(ctx->clk);
if (!ctx->attached_clk)
clk_put(ctx->clk);
}
kfree(context);
}
static const struct regmap_bus regmap_mmio = {
.fast_io = true,
.reg_write = regmap_mmio_write,
.reg_read = regmap_mmio_read,
.reg_noinc_write = regmap_mmio_noinc_write,
.reg_noinc_read = regmap_mmio_noinc_read,
.free_context = regmap_mmio_free_context,
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
};
static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
const char *clk_id,
void __iomem *regs,
const struct regmap_config *config)
{
struct regmap_mmio_context *ctx;
int min_stride;
int ret;
ret = regmap_mmio_regbits_check(config->reg_bits);
if (ret)
return ERR_PTR(ret);
if (config->pad_bits)
return ERR_PTR(-EINVAL);
min_stride = regmap_mmio_get_min_stride(config->val_bits);
if (min_stride < 0)
return ERR_PTR(min_stride);
if (config->reg_stride && config->reg_stride < min_stride)
return ERR_PTR(-EINVAL);
if (config->use_relaxed_mmio && config->io_port)
return ERR_PTR(-EINVAL);
ctx = kzalloc_obj(*ctx);
if (!ctx)
return ERR_PTR(-ENOMEM);
ctx->regs = regs;
ctx->val_bytes = config->val_bits / 8;
ctx->clk = ERR_PTR(-ENODEV);
switch (regmap_get_val_endian(dev, &regmap_mmio, config)) {
case REGMAP_ENDIAN_DEFAULT:
case REGMAP_ENDIAN_LITTLE:
#ifdef __LITTLE_ENDIAN
case REGMAP_ENDIAN_NATIVE:
#endif
switch (config->val_bits) {
case 8:
if (config->io_port) {
ctx->reg_read = regmap_mmio_ioread8;
ctx->reg_write = regmap_mmio_iowrite8;
} else if (config->use_relaxed_mmio) {
ctx->reg_read = regmap_mmio_read8_relaxed;
ctx->reg_write = regmap_mmio_write8_relaxed;
} else {
ctx->reg_read = regmap_mmio_read8;
ctx->reg_write = regmap_mmio_write8;
}
break;
case 16:
if (config->io_port) {
ctx->reg_read = regmap_mmio_ioread16le;
ctx->reg_write = regmap_mmio_iowrite16le;
} else if (config->use_relaxed_mmio) {
ctx->reg_read = regmap_mmio_read16le_relaxed;
ctx->reg_write = regmap_mmio_write16le_relaxed;
} else {
ctx->reg_read = regmap_mmio_read16le;
ctx->reg_write = regmap_mmio_write16le;
}
break;
case 32:
if (config->io_port) {
ctx->reg_read = regmap_mmio_ioread32le;
ctx->reg_write = regmap_mmio_iowrite32le;
} else if (config->use_relaxed_mmio) {
ctx->reg_read = regmap_mmio_read32le_relaxed;
ctx->reg_write = regmap_mmio_write32le_relaxed;
} else {
ctx->reg_read = regmap_mmio_read32le;
ctx->reg_write = regmap_mmio_write32le;
}
break;
default:
ret = -EINVAL;
goto err_free;
}
break;
case REGMAP_ENDIAN_BIG:
#ifdef __BIG_ENDIAN
case REGMAP_ENDIAN_NATIVE:
#endif
ctx->big_endian = true;
switch (config->val_bits) {
case 8:
if (config->io_port) {
ctx->reg_read = regmap_mmio_ioread8;
ctx->reg_write = regmap_mmio_iowrite8;
} else {
ctx->reg_read = regmap_mmio_read8;
ctx->reg_write = regmap_mmio_write8;
}
break;
case 16:
if (config->io_port) {
ctx->reg_read = regmap_mmio_ioread16be;
ctx->reg_write = regmap_mmio_iowrite16be;
} else {
ctx->reg_read = regmap_mmio_read16be;
ctx->reg_write = regmap_mmio_write16be;
}
break;
case 32:
if (config->io_port) {
ctx->reg_read = regmap_mmio_ioread32be;
ctx->reg_write = regmap_mmio_iowrite32be;
} else {
ctx->reg_read = regmap_mmio_read32be;
ctx->reg_write = regmap_mmio_write32be;
}
break;
default:
ret = -EINVAL;
goto err_free;
}
break;
default:
ret = -EINVAL;
goto err_free;
}
if (clk_id == NULL)
return ctx;
ctx->clk = clk_get(dev, clk_id);
if (IS_ERR(ctx->clk)) {
ret = PTR_ERR(ctx->clk);
goto err_free;
}
ret = clk_prepare(ctx->clk);
if (ret < 0) {
clk_put(ctx->clk);
goto err_free;
}
return ctx;
err_free:
kfree(ctx);
return ERR_PTR(ret);
}
struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap_mmio_context *ctx;
ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
if (IS_ERR(ctx))
return ERR_CAST(ctx);
return __regmap_init(dev, &regmap_mmio, ctx, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk);
struct regmap *__devm_regmap_init_mmio_clk(struct device *dev,
const char *clk_id,
void __iomem *regs,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap_mmio_context *ctx;
ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
if (IS_ERR(ctx))
return ERR_CAST(ctx);
return __devm_regmap_init(dev, &regmap_mmio, ctx, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk);
int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk)
{
struct regmap_mmio_context *ctx = map->bus_context;
ctx->clk = clk;
ctx->attached_clk = true;
return clk_prepare(ctx->clk);
}
EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk);
void regmap_mmio_detach_clk(struct regmap *map)
{
struct regmap_mmio_context *ctx = map->bus_context;
clk_unprepare(ctx->clk);
ctx->attached_clk = false;
ctx->clk = NULL;
}
EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk);
MODULE_DESCRIPTION("regmap MMIO Module");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,91 @@
// 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(struct device *dev,
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 = kzalloc_objs(bool, config->max_register + 1);
if (!data->read)
return ERR_PTR(-ENOMEM);
data->written = kzalloc_objs(bool, config->max_register + 1);
if (!data->written) {
kfree(data->read);
return ERR_PTR(-ENOMEM);
}
map = __regmap_init(dev, &regmap_ram, data, config,
lock_key, lock_name);
if (IS_ERR(map)) {
kfree(data->read);
kfree(data->written);
}
return map;
}
EXPORT_SYMBOL_GPL(__regmap_init_ram);
MODULE_DESCRIPTION("Register map access API - Memory region");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,144 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register map access API - Memory region with raw access
//
// This is intended for testing only
//
// Copyright (c) 2023, Arm Ltd
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/swab.h>
#include "internal.h"
static unsigned int decode_reg(enum regmap_endian endian, const void *reg)
{
const u16 *r = reg;
if (endian == REGMAP_ENDIAN_BIG)
return be16_to_cpu(*r);
else
return le16_to_cpu(*r);
}
static int regmap_raw_ram_gather_write(void *context,
const void *reg, size_t reg_len,
const void *val, size_t val_len)
{
struct regmap_ram_data *data = context;
unsigned int r;
u16 *our_buf = (u16 *)data->vals;
int i;
if (reg_len != 2)
return -EINVAL;
if (val_len % 2)
return -EINVAL;
r = decode_reg(data->reg_endian, reg);
if (data->noinc_reg && data->noinc_reg(data, r)) {
memcpy(&our_buf[r], val + val_len - 2, 2);
data->written[r] = true;
} else {
memcpy(&our_buf[r], val, val_len);
for (i = 0; i < val_len / 2; i++)
data->written[r + i] = true;
}
return 0;
}
static int regmap_raw_ram_write(void *context, const void *data, size_t count)
{
return regmap_raw_ram_gather_write(context, data, 2,
data + 2, count - 2);
}
static int regmap_raw_ram_read(void *context,
const void *reg, size_t reg_len,
void *val, size_t val_len)
{
struct regmap_ram_data *data = context;
unsigned int r;
u16 *our_buf = (u16 *)data->vals;
int i;
if (reg_len != 2)
return -EINVAL;
if (val_len % 2)
return -EINVAL;
r = decode_reg(data->reg_endian, reg);
if (data->noinc_reg && data->noinc_reg(data, r)) {
for (i = 0; i < val_len; i += 2)
memcpy(val + i, &our_buf[r], 2);
data->read[r] = true;
} else {
memcpy(val, &our_buf[r], val_len);
for (i = 0; i < val_len / 2; i++)
data->read[r + i] = true;
}
return 0;
}
static void regmap_raw_ram_free_context(void *context)
{
struct regmap_ram_data *data = context;
kfree(data->vals);
kfree(data->read);
kfree(data->written);
kfree(data);
}
static const struct regmap_bus regmap_raw_ram = {
.fast_io = true,
.write = regmap_raw_ram_write,
.gather_write = regmap_raw_ram_gather_write,
.read = regmap_raw_ram_read,
.free_context = regmap_raw_ram_free_context,
};
struct regmap *__regmap_init_raw_ram(struct device *dev,
const struct regmap_config *config,
struct regmap_ram_data *data,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap *map;
if (config->reg_bits != 16)
return ERR_PTR(-EINVAL);
if (!config->max_register) {
pr_crit("No max_register specified for RAM regmap\n");
return ERR_PTR(-EINVAL);
}
data->read = kzalloc_objs(bool, config->max_register + 1);
if (!data->read)
return ERR_PTR(-ENOMEM);
data->written = kzalloc_objs(bool, config->max_register + 1);
if (!data->written)
return ERR_PTR(-ENOMEM);
data->reg_endian = config->reg_format_endian;
map = __regmap_init(dev, &regmap_raw_ram, data, config,
lock_key, lock_name);
return map;
}
EXPORT_SYMBOL_GPL(__regmap_init_raw_ram);
MODULE_DESCRIPTION("Register map access API - Memory region with raw access");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-2.0
// Register map access API - SCCB support
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "internal.h"
/**
* sccb_is_available - Check if the adapter supports SCCB protocol
* @adap: I2C adapter
*
* Return true if the I2C adapter is capable of using SCCB helper functions,
* false otherwise.
*/
static bool sccb_is_available(struct i2c_adapter *adap)
{
u32 needed_funcs = I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
/*
* If we ever want support for hardware doing SCCB natively, we will
* introduce a sccb_xfer() callback to struct i2c_algorithm and check
* for it here.
*/
return (i2c_get_functionality(adap) & needed_funcs) == needed_funcs;
}
/**
* regmap_sccb_read - Read data from SCCB slave device
* @context: Device that will be interacted with
* @reg: Register to be read from
* @val: Pointer to store read value
*
* This executes the 2-phase write transmission cycle that is followed by a
* 2-phase read transmission cycle, returning negative errno else zero on
* success.
*/
static int regmap_sccb_read(void *context, unsigned int reg, unsigned int *val)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
int ret;
union i2c_smbus_data data;
i2c_lock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
ret = __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags,
I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE, NULL);
if (ret < 0)
goto out;
ret = __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags,
I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
if (ret < 0)
goto out;
*val = data.byte;
out:
i2c_unlock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
return ret;
}
/**
* regmap_sccb_write - Write data to SCCB slave device
* @context: Device that will be interacted with
* @reg: Register to write to
* @val: Value to be written
*
* This executes the SCCB 3-phase write transmission cycle, returning negative
* errno else zero on success.
*/
static int regmap_sccb_write(void *context, unsigned int reg, unsigned int val)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
return i2c_smbus_write_byte_data(i2c, reg, val);
}
static const struct regmap_bus regmap_sccb_bus = {
.reg_write = regmap_sccb_write,
.reg_read = regmap_sccb_read,
};
static const struct regmap_bus *regmap_get_sccb_bus(struct i2c_client *i2c,
const struct regmap_config *config)
{
if (config->val_bits == 8 && config->reg_bits == 8 &&
sccb_is_available(i2c->adapter))
return &regmap_sccb_bus;
return ERR_PTR(-ENOTSUPP);
}
struct regmap *__regmap_init_sccb(struct i2c_client *i2c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_sccb_bus(i2c, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __regmap_init(&i2c->dev, bus, &i2c->dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_sccb);
struct regmap *__devm_regmap_init_sccb(struct i2c_client *i2c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_sccb_bus(i2c, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __devm_regmap_init(&i2c->dev, bus, &i2c->dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sccb);
MODULE_DESCRIPTION("Register map access API - SCCB support");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,278 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright(c) 2020 Intel Corporation.
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
#include <sound/sdca_function.h>
#include "internal.h"
struct regmap_mbq_context {
struct device *dev;
struct sdw_slave *sdw;
bool (*readable_reg)(struct device *dev, unsigned int reg);
struct regmap_sdw_mbq_cfg cfg;
int val_size;
};
static int regmap_sdw_mbq_size(struct regmap_mbq_context *ctx, unsigned int reg)
{
int size = ctx->val_size;
if (ctx->cfg.mbq_size) {
size = ctx->cfg.mbq_size(ctx->dev, reg);
if (!size || size > ctx->val_size)
return -EINVAL;
}
return size;
}
static bool regmap_sdw_mbq_deferrable(struct regmap_mbq_context *ctx, unsigned int reg)
{
if (ctx->cfg.deferrable)
return ctx->cfg.deferrable(ctx->dev, reg);
return false;
}
static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg,
struct regmap_mbq_context *ctx)
{
struct device *dev = ctx->dev;
int val, ret = 0;
dev_dbg(dev, "Deferring transaction for 0x%x\n", reg);
reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(reg), 0,
SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
if (!ctx->readable_reg || ctx->readable_reg(dev, reg)) {
ret = read_poll_timeout(sdw_read_no_pm, val,
val < 0 || !(val & SDCA_CTL_ENTITY_0_FUNCTION_BUSY),
ctx->cfg.retry_us, ctx->cfg.timeout_us,
false, slave, reg);
if (val < 0)
return val;
if (ret)
dev_err(dev, "Function busy timed out 0x%x: %d\n", reg, val);
} else {
fsleep(ctx->cfg.timeout_us);
}
return ret;
}
static int regmap_sdw_mbq_write_impl(struct sdw_slave *slave,
unsigned int reg, unsigned int val,
int mbq_size)
{
int shift = mbq_size * BITS_PER_BYTE;
int ret;
while (--mbq_size > 0) {
shift -= BITS_PER_BYTE;
ret = sdw_write_no_pm(slave, SDW_SDCA_MBQ_CTL(reg),
(val >> shift) & 0xff);
if (ret < 0)
return ret;
}
return sdw_write_no_pm(slave, reg, val & 0xff);
}
static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val)
{
struct regmap_mbq_context *ctx = context;
struct sdw_slave *slave = ctx->sdw;
struct device *dev = ctx->dev;
bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
int mbq_size = regmap_sdw_mbq_size(ctx, reg);
int ret;
if (mbq_size < 0)
return mbq_size;
/*
* Technically the spec does allow a device to set itself to busy for
* internal reasons, but since it doesn't provide any information on
* how to handle timeouts in that case, for now the code will only
* process a single wait/timeout on function busy and a single retry
* of the transaction.
*/
ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size);
if (ret == -ENODATA) {
if (!deferrable)
dev_warn(dev, "Defer on undeferrable control: %x\n", reg);
ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx);
if (ret)
return ret;
ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size);
}
return ret;
}
static int regmap_sdw_mbq_read_impl(struct sdw_slave *slave,
unsigned int reg, unsigned int *val,
int mbq_size)
{
int shift = BITS_PER_BYTE;
int read;
read = sdw_read_no_pm(slave, reg);
if (read < 0)
return read;
*val = read;
while (--mbq_size > 0) {
read = sdw_read_no_pm(slave, SDW_SDCA_MBQ_CTL(reg));
if (read < 0)
return read;
*val |= read << shift;
shift += BITS_PER_BYTE;
}
return 0;
}
static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)
{
struct regmap_mbq_context *ctx = context;
struct sdw_slave *slave = ctx->sdw;
struct device *dev = ctx->dev;
bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
int mbq_size = regmap_sdw_mbq_size(ctx, reg);
int ret;
if (mbq_size < 0)
return mbq_size;
/*
* Technically the spec does allow a device to set itself to busy for
* internal reasons, but since it doesn't provide any information on
* how to handle timeouts in that case, for now the code will only
* process a single wait/timeout on function busy and a single retry
* of the transaction.
*/
ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size);
if (ret == -ENODATA) {
if (!deferrable)
dev_warn(dev, "Defer on undeferrable control: %x\n", reg);
ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx);
if (ret)
return ret;
ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size);
}
return ret;
}
static const struct regmap_bus regmap_sdw_mbq = {
.reg_read = regmap_sdw_mbq_read,
.reg_write = regmap_sdw_mbq_write,
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
};
static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
{
if (config->val_bits > (sizeof(unsigned int) * BITS_PER_BYTE))
return -ENOTSUPP;
/* Registers are 32 bits wide */
if (config->reg_bits != 32)
return -ENOTSUPP;
if (config->pad_bits != 0)
return -ENOTSUPP;
return 0;
}
static struct regmap_mbq_context *
regmap_sdw_mbq_gen_context(struct device *dev,
struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config)
{
struct regmap_mbq_context *ctx;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return ERR_PTR(-ENOMEM);
ctx->dev = dev;
ctx->sdw = sdw;
if (mbq_config)
ctx->cfg = *mbq_config;
ctx->val_size = config->val_bits / BITS_PER_BYTE;
ctx->readable_reg = config->readable_reg;
return ctx;
}
struct regmap *__regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap_mbq_context *ctx;
int ret;
ret = regmap_sdw_mbq_config_check(config);
if (ret)
return ERR_PTR(ret);
ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config);
if (IS_ERR(ctx))
return ERR_CAST(ctx);
return __regmap_init(dev, &regmap_sdw_mbq, ctx,
config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_sdw_mbq);
struct regmap *__devm_regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap_mbq_context *ctx;
int ret;
ret = regmap_sdw_mbq_config_check(config);
if (ret)
return ERR_PTR(ret);
ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config);
if (IS_ERR(ctx))
return ERR_CAST(ctx);
return __devm_regmap_init(dev, &regmap_sdw_mbq, ctx,
config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq);
MODULE_DESCRIPTION("regmap SoundWire MBQ Module");
MODULE_LICENSE("GPL");
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright(c) 2015-17 Intel Corporation.
#include <linux/device.h>
#include <linux/errno.h>
#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, 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_nwrite_no_pm(slave, addr, val_size - sizeof(addr), val + sizeof(addr));
}
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);
u32 addr = le32_to_cpu(*(const __le32 *)reg_buf);
return sdw_nwrite_no_pm(slave, addr, val_size, val_buf);
}
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 = {
.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)
{
/* Register addresses are 32 bits wide */
if (config->reg_bits != 32)
return -ENOTSUPP;
if (config->pad_bits != 0)
return -ENOTSUPP;
/* Only bulk writes are supported not multi-register writes */
if (config->can_multi_write)
return -ENOTSUPP;
return 0;
}
struct regmap *__regmap_init_sdw(struct sdw_slave *sdw,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
int ret;
ret = regmap_sdw_config_check(config);
if (ret)
return ERR_PTR(ret);
return __regmap_init(&sdw->dev, &regmap_sdw,
&sdw->dev, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_sdw);
struct regmap *__devm_regmap_init_sdw(struct sdw_slave *sdw,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
int ret;
ret = regmap_sdw_config_check(config);
if (ret)
return ERR_PTR(ret);
return __devm_regmap_init(&sdw->dev, &regmap_sdw,
&sdw->dev, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw);
MODULE_DESCRIPTION("regmap SoundWire Module");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2017, Linaro Ltd.
#include <linux/regmap.h>
#include <linux/slimbus.h>
#include <linux/module.h>
#include "internal.h"
static int regmap_slimbus_write(void *context, const void *data, size_t count)
{
struct slim_device *sdev = context;
return slim_write(sdev, *(u16 *)data, count - 2, (u8 *)data + 2);
}
static int regmap_slimbus_read(void *context, const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct slim_device *sdev = context;
return slim_read(sdev, *(u16 *)reg, val_size, val);
}
static const struct regmap_bus regmap_slimbus_bus = {
.write = regmap_slimbus_write,
.read = regmap_slimbus_read,
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
};
static const struct regmap_bus *regmap_get_slimbus(struct slim_device *slim,
const struct regmap_config *config)
{
if (config->val_bits == 8 && config->reg_bits == 16)
return &regmap_slimbus_bus;
return ERR_PTR(-ENOTSUPP);
}
struct regmap *__regmap_init_slimbus(struct slim_device *slimbus,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_slimbus(slimbus, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __regmap_init(&slimbus->dev, bus, slimbus, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_slimbus);
struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_slimbus(slimbus, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __devm_regmap_init(&slimbus->dev, bus, slimbus, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_slimbus);
MODULE_DESCRIPTION("Register map access API - SLIMbus support");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,714 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register map access API - SPI AVMM support
//
// Copyright (C) 2018-2020 Intel Corporation. All rights reserved.
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/swab.h>
/*
* This driver implements the regmap operations for a generic SPI
* master to access the registers of the spi slave chip which has an
* Avalone bus in it.
*
* The "SPI slave to Avalon Master Bridge" (spi-avmm) IP should be integrated
* in the spi slave chip. The IP acts as a bridge to convert encoded streams of
* bytes from the host to the internal register read/write on Avalon bus. In
* order to issue register access requests to the slave chip, the host should
* send formatted bytes that conform to the transfer protocol.
* The transfer protocol contains 3 layers: transaction layer, packet layer
* and physical layer.
*
* Reference Documents could be found at:
* https://www.intel.com/content/www/us/en/programmable/documentation/sfo1400787952932.html
*
* Chapter "SPI Slave/JTAG to Avalon Master Bridge Cores" is a general
* introduction to the protocol.
*
* Chapter "Avalon Packets to Transactions Converter Core" describes
* the transaction layer.
*
* Chapter "Avalon-ST Bytes to Packets and Packets to Bytes Converter Cores"
* describes the packet layer.
*
* Chapter "Avalon-ST Serial Peripheral Interface Core" describes the
* physical layer.
*
*
* When host issues a regmap read/write, the driver will transform the request
* to byte stream layer by layer. It formats the register addr, value and
* length to the transaction layer request, then converts the request to packet
* layer bytes stream and then to physical layer bytes stream. Finally the
* driver sends the formatted byte stream over SPI bus to the slave chip.
*
* The spi-avmm IP on the slave chip decodes the byte stream and initiates
* register read/write on its internal Avalon bus, and then encodes the
* response to byte stream and sends back to host.
*
* The driver receives the byte stream, reverses the 3 layers transformation,
* and finally gets the response value (read out data for register read,
* successful written size for register write).
*/
#define PKT_SOP 0x7a
#define PKT_EOP 0x7b
#define PKT_CHANNEL 0x7c
#define PKT_ESC 0x7d
#define PHY_IDLE 0x4a
#define PHY_ESC 0x4d
#define TRANS_CODE_WRITE 0x0
#define TRANS_CODE_SEQ_WRITE 0x4
#define TRANS_CODE_READ 0x10
#define TRANS_CODE_SEQ_READ 0x14
#define TRANS_CODE_NO_TRANS 0x7f
#define SPI_AVMM_XFER_TIMEOUT (msecs_to_jiffies(200))
/* slave's register addr is 32 bits */
#define SPI_AVMM_REG_SIZE 4UL
/* slave's register value is 32 bits */
#define SPI_AVMM_VAL_SIZE 4UL
/*
* max rx size could be larger. But considering the buffer consuming,
* it is proper that we limit 1KB xfer at max.
*/
#define MAX_READ_CNT 256UL
#define MAX_WRITE_CNT 1UL
struct trans_req_header {
u8 code;
u8 rsvd;
__be16 size;
__be32 addr;
} __packed;
struct trans_resp_header {
u8 r_code;
u8 rsvd;
__be16 size;
} __packed;
#define TRANS_REQ_HD_SIZE (sizeof(struct trans_req_header))
#define TRANS_RESP_HD_SIZE (sizeof(struct trans_resp_header))
/*
* In transaction layer,
* the write request format is: Transaction request header + data
* the read request format is: Transaction request header
* the write response format is: Transaction response header
* the read response format is: pure data, no Transaction response header
*/
#define TRANS_WR_TX_SIZE(n) (TRANS_REQ_HD_SIZE + SPI_AVMM_VAL_SIZE * (n))
#define TRANS_RD_TX_SIZE TRANS_REQ_HD_SIZE
#define TRANS_TX_MAX TRANS_WR_TX_SIZE(MAX_WRITE_CNT)
#define TRANS_RD_RX_SIZE(n) (SPI_AVMM_VAL_SIZE * (n))
#define TRANS_WR_RX_SIZE TRANS_RESP_HD_SIZE
#define TRANS_RX_MAX TRANS_RD_RX_SIZE(MAX_READ_CNT)
/* tx & rx share one transaction layer buffer */
#define TRANS_BUF_SIZE ((TRANS_TX_MAX > TRANS_RX_MAX) ? \
TRANS_TX_MAX : TRANS_RX_MAX)
/*
* In tx phase, the host prepares all the phy layer bytes of a request in the
* phy buffer and sends them in a batch.
*
* The packet layer and physical layer defines several special chars for
* various purpose, when a transaction layer byte hits one of these special
* chars, it should be escaped. The escape rule is, "Escape char first,
* following the byte XOR'ed with 0x20".
*
* This macro defines the max possible length of the phy data. In the worst
* case, all transaction layer bytes need to be escaped (so the data length
* doubles), plus 4 special chars (SOP, CHANNEL, CHANNEL_NUM, EOP). Finally
* we should make sure the length is aligned to SPI BPW.
*/
#define PHY_TX_MAX ALIGN(2 * TRANS_TX_MAX + 4, 4)
/*
* Unlike tx, phy rx is affected by possible PHY_IDLE bytes from slave, the max
* length of the rx bit stream is unpredictable. So the driver reads the words
* one by one, and parses each word immediately into transaction layer buffer.
* Only one word length of phy buffer is used for rx.
*/
#define PHY_BUF_SIZE PHY_TX_MAX
/**
* struct spi_avmm_bridge - SPI slave to AVMM bus master bridge
*
* @spi: spi slave associated with this bridge.
* @word_len: bytes of word for spi transfer.
* @trans_len: length of valid data in trans_buf.
* @phy_len: length of valid data in phy_buf.
* @trans_buf: the bridge buffer for transaction layer data.
* @phy_buf: the bridge buffer for physical layer data.
* @swap_words: the word swapping cb for phy data. NULL if not needed.
*
* As a device's registers are implemented on the AVMM bus address space, it
* requires the driver to issue formatted requests to spi slave to AVMM bus
* master bridge to perform register access.
*/
struct spi_avmm_bridge {
struct spi_device *spi;
unsigned char word_len;
unsigned int trans_len;
unsigned int phy_len;
/* bridge buffer used in translation between protocol layers */
char trans_buf[TRANS_BUF_SIZE];
char phy_buf[PHY_BUF_SIZE];
void (*swap_words)(void *buf, unsigned int len);
};
static void br_swap_words_32(void *buf, unsigned int len)
{
swab32_array(buf, len / 4);
}
/*
* Format transaction layer data in br->trans_buf according to the register
* access request, Store valid transaction layer data length in br->trans_len.
*/
static int br_trans_tx_prepare(struct spi_avmm_bridge *br, bool is_read, u32 reg,
u32 *wr_val, u32 count)
{
struct trans_req_header *header;
unsigned int trans_len;
u8 code;
__le32 *data;
int i;
if (is_read) {
if (count == 1)
code = TRANS_CODE_READ;
else
code = TRANS_CODE_SEQ_READ;
} else {
if (count == 1)
code = TRANS_CODE_WRITE;
else
code = TRANS_CODE_SEQ_WRITE;
}
header = (struct trans_req_header *)br->trans_buf;
header->code = code;
header->rsvd = 0;
header->size = cpu_to_be16((u16)count * SPI_AVMM_VAL_SIZE);
header->addr = cpu_to_be32(reg);
trans_len = TRANS_REQ_HD_SIZE;
if (!is_read) {
trans_len += SPI_AVMM_VAL_SIZE * count;
if (trans_len > sizeof(br->trans_buf))
return -ENOMEM;
data = (__le32 *)(br->trans_buf + TRANS_REQ_HD_SIZE);
for (i = 0; i < count; i++)
*data++ = cpu_to_le32(*wr_val++);
}
/* Store valid trans data length for next layer */
br->trans_len = trans_len;
return 0;
}
/*
* Convert transaction layer data (in br->trans_buf) to phy layer data, store
* them in br->phy_buf. Pad the phy_buf aligned with SPI's BPW. Store valid phy
* layer data length in br->phy_len.
*
* phy_buf len should be aligned with SPI's BPW. Spare bytes should be padded
* with PHY_IDLE, then the slave will just drop them.
*
* The driver will not simply pad 4a at the tail. The concern is that driver
* will not store MISO data during tx phase, if the driver pads 4a at the tail,
* it is possible that if the slave is fast enough to response at the padding
* time. As a result these rx bytes are lost. In the following case, 7a,7c,00
* will lost.
* MOSI ...|7a|7c|00|10| |00|00|04|02| |4b|7d|5a|7b| |40|4a|4a|4a| |XX|XX|...
* MISO ...|4a|4a|4a|4a| |4a|4a|4a|4a| |4a|4a|4a|4a| |4a|7a|7c|00| |78|56|...
*
* So the driver moves EOP and bytes after EOP to the end of the aligned size,
* then fill the hole with PHY_IDLE. As following:
* before pad ...|7a|7c|00|10| |00|00|04|02| |4b|7d|5a|7b| |40|
* after pad ...|7a|7c|00|10| |00|00|04|02| |4b|7d|5a|4a| |4a|4a|7b|40|
* Then if the slave will not get the entire packet before the tx phase is
* over, it can't responsed to anything either.
*/
static int br_pkt_phy_tx_prepare(struct spi_avmm_bridge *br)
{
char *tb, *tb_end, *pb, *pb_limit, *pb_eop = NULL;
unsigned int aligned_phy_len, move_size;
bool need_esc = false;
tb = br->trans_buf;
tb_end = tb + br->trans_len;
pb = br->phy_buf;
pb_limit = pb + ARRAY_SIZE(br->phy_buf);
*pb++ = PKT_SOP;
/*
* The driver doesn't support multiple channels so the channel number
* is always 0.
*/
*pb++ = PKT_CHANNEL;
*pb++ = 0x0;
for (; pb < pb_limit && tb < tb_end; pb++) {
if (need_esc) {
*pb = *tb++ ^ 0x20;
need_esc = false;
continue;
}
/* EOP should be inserted before the last valid char */
if (tb == tb_end - 1 && !pb_eop) {
*pb = PKT_EOP;
pb_eop = pb;
continue;
}
/*
* insert an ESCAPE char if the data value equals any special
* char.
*/
switch (*tb) {
case PKT_SOP:
case PKT_EOP:
case PKT_CHANNEL:
case PKT_ESC:
*pb = PKT_ESC;
need_esc = true;
break;
case PHY_IDLE:
case PHY_ESC:
*pb = PHY_ESC;
need_esc = true;
break;
default:
*pb = *tb++;
break;
}
}
/* The phy buffer is used out but transaction layer data remains */
if (tb < tb_end)
return -ENOMEM;
/* Store valid phy data length for spi transfer */
br->phy_len = pb - br->phy_buf;
if (br->word_len == 1)
return 0;
/* Do phy buf padding if word_len > 1 byte. */
aligned_phy_len = ALIGN(br->phy_len, br->word_len);
if (aligned_phy_len > sizeof(br->phy_buf))
return -ENOMEM;
if (aligned_phy_len == br->phy_len)
return 0;
/* move EOP and bytes after EOP to the end of aligned size */
move_size = pb - pb_eop;
memmove(&br->phy_buf[aligned_phy_len - move_size], pb_eop, move_size);
/* fill the hole with PHY_IDLEs */
memset(pb_eop, PHY_IDLE, aligned_phy_len - br->phy_len);
/* update the phy data length */
br->phy_len = aligned_phy_len;
return 0;
}
/*
* In tx phase, the slave only returns PHY_IDLE (0x4a). So the driver will
* ignore rx in tx phase.
*/
static int br_do_tx(struct spi_avmm_bridge *br)
{
/* reorder words for spi transfer */
if (br->swap_words)
br->swap_words(br->phy_buf, br->phy_len);
/* send all data in phy_buf */
return spi_write(br->spi, br->phy_buf, br->phy_len);
}
/*
* This function read the rx byte stream from SPI word by word and convert
* them to transaction layer data in br->trans_buf. It also stores the length
* of rx transaction layer data in br->trans_len
*
* The slave may send an unknown number of PHY_IDLEs in rx phase, so we cannot
* prepare a fixed length buffer to receive all of the rx data in a batch. We
* have to read word by word and convert them to transaction layer data at
* once.
*/
static int br_do_rx_and_pkt_phy_parse(struct spi_avmm_bridge *br)
{
bool eop_found = false, channel_found = false, esc_found = false;
bool valid_word = false, last_try = false;
struct device *dev = &br->spi->dev;
char *pb, *tb_limit, *tb = NULL;
unsigned long poll_timeout;
int ret, i;
tb_limit = br->trans_buf + ARRAY_SIZE(br->trans_buf);
pb = br->phy_buf;
poll_timeout = jiffies + SPI_AVMM_XFER_TIMEOUT;
while (tb < tb_limit) {
ret = spi_read(br->spi, pb, br->word_len);
if (ret)
return ret;
/* reorder the word back */
if (br->swap_words)
br->swap_words(pb, br->word_len);
valid_word = false;
for (i = 0; i < br->word_len; i++) {
/* drop everything before first SOP */
if (!tb && pb[i] != PKT_SOP)
continue;
/* drop PHY_IDLE */
if (pb[i] == PHY_IDLE)
continue;
valid_word = true;
/*
* We don't support multiple channels, so error out if
* a non-zero channel number is found.
*/
if (channel_found) {
if (pb[i] != 0) {
dev_err(dev, "%s channel num != 0\n",
__func__);
return -EFAULT;
}
channel_found = false;
continue;
}
switch (pb[i]) {
case PKT_SOP:
/*
* reset the parsing if a second SOP appears.
*/
tb = br->trans_buf;
eop_found = false;
channel_found = false;
esc_found = false;
break;
case PKT_EOP:
/*
* No special char is expected after ESC char.
* No special char (except ESC & PHY_IDLE) is
* expected after EOP char.
*
* The special chars are all dropped.
*/
if (esc_found || eop_found)
return -EFAULT;
eop_found = true;
break;
case PKT_CHANNEL:
if (esc_found || eop_found)
return -EFAULT;
channel_found = true;
break;
case PKT_ESC:
case PHY_ESC:
if (esc_found)
return -EFAULT;
esc_found = true;
break;
default:
/* Record the normal byte in trans_buf. */
if (esc_found) {
*tb++ = pb[i] ^ 0x20;
esc_found = false;
} else {
*tb++ = pb[i];
}
/*
* We get the last normal byte after EOP, it is
* time we finish. Normally the function should
* return here.
*/
if (eop_found) {
br->trans_len = tb - br->trans_buf;
return 0;
}
}
}
if (valid_word) {
/* update poll timeout when we get valid word */
poll_timeout = jiffies + SPI_AVMM_XFER_TIMEOUT;
last_try = false;
} else {
/*
* We timeout when rx keeps invalid for some time. But
* it is possible we are scheduled out for long time
* after a spi_read. So when we are scheduled in, a SW
* timeout happens. But actually HW may have worked fine and
* has been ready long time ago. So we need to do an extra
* read, if we get a valid word then we could continue rx,
* otherwise real a HW issue happens.
*/
if (last_try)
return -ETIMEDOUT;
if (time_after(jiffies, poll_timeout))
last_try = true;
}
}
/*
* We have used out all transfer layer buffer but cannot find the end
* of the byte stream.
*/
dev_err(dev, "%s transfer buffer is full but rx doesn't end\n",
__func__);
return -EFAULT;
}
/*
* For read transactions, the avmm bus will directly return register values
* without transaction response header.
*/
static int br_rd_trans_rx_parse(struct spi_avmm_bridge *br,
u32 *val, unsigned int expected_count)
{
unsigned int i, trans_len = br->trans_len;
__le32 *data;
if (expected_count * SPI_AVMM_VAL_SIZE != trans_len)
return -EFAULT;
data = (__le32 *)br->trans_buf;
for (i = 0; i < expected_count; i++)
*val++ = le32_to_cpu(*data++);
return 0;
}
/*
* For write transactions, the slave will return a transaction response
* header.
*/
static int br_wr_trans_rx_parse(struct spi_avmm_bridge *br,
unsigned int expected_count)
{
unsigned int trans_len = br->trans_len;
struct trans_resp_header *resp;
u8 code;
u16 val_len;
if (trans_len != TRANS_RESP_HD_SIZE)
return -EFAULT;
resp = (struct trans_resp_header *)br->trans_buf;
code = resp->r_code ^ 0x80;
val_len = be16_to_cpu(resp->size);
if (!val_len || val_len != expected_count * SPI_AVMM_VAL_SIZE)
return -EFAULT;
/* error out if the trans code doesn't align with the val size */
if ((val_len == SPI_AVMM_VAL_SIZE && code != TRANS_CODE_WRITE) ||
(val_len > SPI_AVMM_VAL_SIZE && code != TRANS_CODE_SEQ_WRITE))
return -EFAULT;
return 0;
}
static int do_reg_access(void *context, bool is_read, unsigned int reg,
unsigned int *value, unsigned int count)
{
struct spi_avmm_bridge *br = context;
int ret;
/* invalidate bridge buffers first */
br->trans_len = 0;
br->phy_len = 0;
ret = br_trans_tx_prepare(br, is_read, reg, value, count);
if (ret)
return ret;
ret = br_pkt_phy_tx_prepare(br);
if (ret)
return ret;
ret = br_do_tx(br);
if (ret)
return ret;
ret = br_do_rx_and_pkt_phy_parse(br);
if (ret)
return ret;
if (is_read)
return br_rd_trans_rx_parse(br, value, count);
else
return br_wr_trans_rx_parse(br, count);
}
static int regmap_spi_avmm_gather_write(void *context,
const void *reg_buf, size_t reg_len,
const void *val_buf, size_t val_len)
{
if (reg_len != SPI_AVMM_REG_SIZE)
return -EINVAL;
if (!IS_ALIGNED(val_len, SPI_AVMM_VAL_SIZE))
return -EINVAL;
return do_reg_access(context, false, *(u32 *)reg_buf, (u32 *)val_buf,
val_len / SPI_AVMM_VAL_SIZE);
}
static int regmap_spi_avmm_write(void *context, const void *data, size_t bytes)
{
if (bytes < SPI_AVMM_REG_SIZE + SPI_AVMM_VAL_SIZE)
return -EINVAL;
return regmap_spi_avmm_gather_write(context, data, SPI_AVMM_REG_SIZE,
data + SPI_AVMM_REG_SIZE,
bytes - SPI_AVMM_REG_SIZE);
}
static int regmap_spi_avmm_read(void *context,
const void *reg_buf, size_t reg_len,
void *val_buf, size_t val_len)
{
if (reg_len != SPI_AVMM_REG_SIZE)
return -EINVAL;
if (!IS_ALIGNED(val_len, SPI_AVMM_VAL_SIZE))
return -EINVAL;
return do_reg_access(context, true, *(u32 *)reg_buf, val_buf,
(val_len / SPI_AVMM_VAL_SIZE));
}
static struct spi_avmm_bridge *
spi_avmm_bridge_ctx_gen(struct spi_device *spi)
{
struct spi_avmm_bridge *br;
if (!spi)
return ERR_PTR(-ENODEV);
/* Only support BPW == 8 or 32 now. Try 32 BPW first. */
spi->mode = SPI_MODE_1;
spi->bits_per_word = 32;
if (spi_setup(spi)) {
spi->bits_per_word = 8;
if (spi_setup(spi))
return ERR_PTR(-EINVAL);
}
br = kzalloc_obj(*br);
if (!br)
return ERR_PTR(-ENOMEM);
br->spi = spi;
br->word_len = spi->bits_per_word / 8;
if (br->word_len == 4) {
/*
* The protocol requires little endian byte order but MSB
* first. So driver needs to swap the byte order word by word
* if word length > 1.
*/
br->swap_words = br_swap_words_32;
}
return br;
}
static void spi_avmm_bridge_ctx_free(void *context)
{
kfree(context);
}
static const struct regmap_bus regmap_spi_avmm_bus = {
.write = regmap_spi_avmm_write,
.gather_write = regmap_spi_avmm_gather_write,
.read = regmap_spi_avmm_read,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
.max_raw_read = SPI_AVMM_VAL_SIZE * MAX_READ_CNT,
.max_raw_write = SPI_AVMM_VAL_SIZE * MAX_WRITE_CNT,
.free_context = spi_avmm_bridge_ctx_free,
};
struct regmap *__regmap_init_spi_avmm(struct spi_device *spi,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct spi_avmm_bridge *bridge;
struct regmap *map;
bridge = spi_avmm_bridge_ctx_gen(spi);
if (IS_ERR(bridge))
return ERR_CAST(bridge);
map = __regmap_init(&spi->dev, &regmap_spi_avmm_bus,
bridge, config, lock_key, lock_name);
if (IS_ERR(map)) {
spi_avmm_bridge_ctx_free(bridge);
return ERR_CAST(map);
}
return map;
}
EXPORT_SYMBOL_GPL(__regmap_init_spi_avmm);
struct regmap *__devm_regmap_init_spi_avmm(struct spi_device *spi,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct spi_avmm_bridge *bridge;
struct regmap *map;
bridge = spi_avmm_bridge_ctx_gen(spi);
if (IS_ERR(bridge))
return ERR_CAST(bridge);
map = __devm_regmap_init(&spi->dev, &regmap_spi_avmm_bus,
bridge, config, lock_key, lock_name);
if (IS_ERR(map)) {
spi_avmm_bridge_ctx_free(bridge);
return ERR_CAST(map);
}
return map;
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_spi_avmm);
MODULE_DESCRIPTION("Register map access API - SPI AVMM support");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register map access API - SPI support
//
// Copyright 2011 Wolfson Microelectronics plc
//
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include "internal.h"
struct regmap_async_spi {
struct regmap_async core;
struct spi_message m;
struct spi_transfer t[2];
};
static void regmap_spi_complete(void *data)
{
struct regmap_async_spi *async = data;
regmap_async_complete_cb(&async->core, async->m.status);
}
static int regmap_spi_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
return spi_write(spi, data, count);
}
static int regmap_spi_gather_write(void *context,
const void *reg, size_t reg_len,
const void *val, size_t val_len)
{
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
struct spi_message m;
struct spi_transfer t[2] = { { .tx_buf = reg, .len = reg_len, },
{ .tx_buf = val, .len = val_len, }, };
spi_message_init(&m);
spi_message_add_tail(&t[0], &m);
spi_message_add_tail(&t[1], &m);
return spi_sync(spi, &m);
}
static int regmap_spi_async_write(void *context,
const void *reg, size_t reg_len,
const void *val, size_t val_len,
struct regmap_async *a)
{
struct regmap_async_spi *async = container_of(a,
struct regmap_async_spi,
core);
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
async->t[0].tx_buf = reg;
async->t[0].len = reg_len;
async->t[1].tx_buf = val;
async->t[1].len = val_len;
spi_message_init(&async->m);
spi_message_add_tail(&async->t[0], &async->m);
if (val)
spi_message_add_tail(&async->t[1], &async->m);
async->m.complete = regmap_spi_complete;
async->m.context = async;
return spi_async(spi, &async->m);
}
static struct regmap_async *regmap_spi_async_alloc(void)
{
struct regmap_async_spi *async_spi;
async_spi = kzalloc_obj(*async_spi);
if (!async_spi)
return NULL;
return &async_spi->core;
}
static int regmap_spi_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
return spi_write_then_read(spi, reg, reg_size, val, val_size);
}
static const struct regmap_bus regmap_spi = {
.write = regmap_spi_write,
.gather_write = regmap_spi_gather_write,
.async_write = regmap_spi_async_write,
.async_alloc = regmap_spi_async_alloc,
.read = regmap_spi_read,
.read_flag_mask = 0x80,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
static const struct regmap_bus *regmap_get_spi_bus(struct spi_device *spi,
const struct regmap_config *config)
{
size_t max_size = spi_max_transfer_size(spi);
size_t max_msg_size, reg_reserve_size;
struct regmap_bus *bus;
if (max_size != SIZE_MAX) {
bus = kmemdup(&regmap_spi, sizeof(*bus), GFP_KERNEL);
if (!bus)
return ERR_PTR(-ENOMEM);
max_msg_size = spi_max_message_size(spi);
reg_reserve_size = (config->reg_bits + config->pad_bits) / BITS_PER_BYTE;
if (max_size + reg_reserve_size > max_msg_size)
max_size -= reg_reserve_size;
bus->free_on_exit = true;
bus->max_raw_read = max_size;
bus->max_raw_write = max_size;
return bus;
}
return &regmap_spi;
}
struct regmap *__regmap_init_spi(struct spi_device *spi,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_spi_bus(spi, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __regmap_init(&spi->dev, bus, &spi->dev, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_spi);
struct regmap *__devm_regmap_init_spi(struct spi_device *spi,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_spi_bus(spi, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __devm_regmap_init(&spi->dev, bus, &spi->dev, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_spi);
MODULE_DESCRIPTION("regmap SPI Module");
MODULE_LICENSE("GPL");
@@ -0,0 +1,226 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register map access API - SPMI support
//
// Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
//
// Based on regmap-i2c.c:
// Copyright 2011 Wolfson Microelectronics plc
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
#include <linux/regmap.h>
#include <linux/spmi.h>
#include <linux/module.h>
#include <linux/init.h>
static int regmap_spmi_base_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
u8 addr = *(u8 *)reg;
int err = 0;
BUG_ON(reg_size != 1);
while (val_size-- && !err)
err = spmi_register_read(context, addr++, val++);
return err;
}
static int regmap_spmi_base_gather_write(void *context,
const void *reg, size_t reg_size,
const void *val, size_t val_size)
{
const u8 *data = val;
u8 addr = *(u8 *)reg;
int err = 0;
BUG_ON(reg_size != 1);
/*
* SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence,
* use it when possible.
*/
if (addr == 0 && val_size) {
err = spmi_register_zero_write(context, *data);
if (err)
goto err_out;
data++;
addr++;
val_size--;
}
while (val_size) {
err = spmi_register_write(context, addr, *data);
if (err)
goto err_out;
data++;
addr++;
val_size--;
}
err_out:
return err;
}
static int regmap_spmi_base_write(void *context, const void *data,
size_t count)
{
BUG_ON(count < 1);
return regmap_spmi_base_gather_write(context, data, 1, data + 1,
count - 1);
}
static const struct regmap_bus regmap_spmi_base = {
.read = regmap_spmi_base_read,
.write = regmap_spmi_base_write,
.gather_write = regmap_spmi_base_gather_write,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
struct regmap *__regmap_init_spmi_base(struct spmi_device *sdev,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
return __regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_spmi_base);
struct regmap *__devm_regmap_init_spmi_base(struct spmi_device *sdev,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
return __devm_regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_base);
static int regmap_spmi_ext_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
int err = 0;
size_t len;
u16 addr;
BUG_ON(reg_size != 2);
addr = *(u16 *)reg;
/*
* Split accesses into two to take advantage of the more
* bandwidth-efficient 'Extended Register Read' command when possible
*/
while (addr <= 0xFF && val_size) {
len = min_t(size_t, val_size, 16);
err = spmi_ext_register_read(context, addr, val, len);
if (err)
goto err_out;
addr += len;
val += len;
val_size -= len;
}
while (val_size) {
len = min_t(size_t, val_size, 8);
err = spmi_ext_register_readl(context, addr, val, len);
if (err)
goto err_out;
addr += len;
val += len;
val_size -= len;
}
err_out:
return err;
}
static int regmap_spmi_ext_gather_write(void *context,
const void *reg, size_t reg_size,
const void *val, size_t val_size)
{
int err = 0;
size_t len;
u16 addr;
BUG_ON(reg_size != 2);
addr = *(u16 *)reg;
while (addr <= 0xFF && val_size) {
len = min_t(size_t, val_size, 16);
err = spmi_ext_register_write(context, addr, val, len);
if (err)
goto err_out;
addr += len;
val += len;
val_size -= len;
}
while (val_size) {
len = min_t(size_t, val_size, 8);
err = spmi_ext_register_writel(context, addr, val, len);
if (err)
goto err_out;
addr += len;
val += len;
val_size -= len;
}
err_out:
return err;
}
static int regmap_spmi_ext_write(void *context, const void *data,
size_t count)
{
BUG_ON(count < 2);
return regmap_spmi_ext_gather_write(context, data, 2, data + 2,
count - 2);
}
static const struct regmap_bus regmap_spmi_ext = {
.read = regmap_spmi_ext_read,
.write = regmap_spmi_ext_write,
.gather_write = regmap_spmi_ext_gather_write,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
struct regmap *__regmap_init_spmi_ext(struct spmi_device *sdev,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
return __regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_spmi_ext);
struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
return __devm_regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext);
MODULE_DESCRIPTION("Register map access API - SPMI support");
MODULE_LICENSE("GPL");
+238
View File
@@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-2.0
//
// Register map access API - W1 (1-Wire) support
//
// Copyright (c) 2017 Radioavionica Corporation
// Author: Alex A. Mihaylov <minimumlaw@rambler.ru>
#include <linux/regmap.h>
#include <linux/module.h>
#include <linux/w1.h>
#include "internal.h"
#define W1_CMD_READ_DATA 0x69
#define W1_CMD_WRITE_DATA 0x6C
/*
* 1-Wire slaves registers with addess 8 bit and data 8 bit
*/
static int w1_reg_a8_v8_read(void *context, unsigned int reg, unsigned int *val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 255)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_READ_DATA);
w1_write_8(sl->master, reg);
*val = w1_read_8(sl->master);
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static int w1_reg_a8_v8_write(void *context, unsigned int reg, unsigned int val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 255)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_WRITE_DATA);
w1_write_8(sl->master, reg);
w1_write_8(sl->master, val);
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
/*
* 1-Wire slaves registers with addess 8 bit and data 16 bit
*/
static int w1_reg_a8_v16_read(void *context, unsigned int reg,
unsigned int *val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 255)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_READ_DATA);
w1_write_8(sl->master, reg);
*val = w1_read_8(sl->master);
*val |= w1_read_8(sl->master)<<8;
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static int w1_reg_a8_v16_write(void *context, unsigned int reg,
unsigned int val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 255)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_WRITE_DATA);
w1_write_8(sl->master, reg);
w1_write_8(sl->master, val & 0x00FF);
w1_write_8(sl->master, val>>8 & 0x00FF);
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
/*
* 1-Wire slaves registers with addess 16 bit and data 16 bit
*/
static int w1_reg_a16_v16_read(void *context, unsigned int reg,
unsigned int *val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 65535)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_READ_DATA);
w1_write_8(sl->master, reg & 0x00FF);
w1_write_8(sl->master, reg>>8 & 0x00FF);
*val = w1_read_8(sl->master);
*val |= w1_read_8(sl->master)<<8;
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static int w1_reg_a16_v16_write(void *context, unsigned int reg,
unsigned int val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 65535)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_WRITE_DATA);
w1_write_8(sl->master, reg & 0x00FF);
w1_write_8(sl->master, reg>>8 & 0x00FF);
w1_write_8(sl->master, val & 0x00FF);
w1_write_8(sl->master, val>>8 & 0x00FF);
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
/*
* Various types of supported bus addressing
*/
static const struct regmap_bus regmap_w1_bus_a8_v8 = {
.reg_read = w1_reg_a8_v8_read,
.reg_write = w1_reg_a8_v8_write,
};
static const struct regmap_bus regmap_w1_bus_a8_v16 = {
.reg_read = w1_reg_a8_v16_read,
.reg_write = w1_reg_a8_v16_write,
};
static const struct regmap_bus regmap_w1_bus_a16_v16 = {
.reg_read = w1_reg_a16_v16_read,
.reg_write = w1_reg_a16_v16_write,
};
static const struct regmap_bus *regmap_get_w1_bus(struct device *w1_dev,
const struct regmap_config *config)
{
if (config->reg_bits == 8 && config->val_bits == 8)
return &regmap_w1_bus_a8_v8;
if (config->reg_bits == 8 && config->val_bits == 16)
return &regmap_w1_bus_a8_v16;
if (config->reg_bits == 16 && config->val_bits == 16)
return &regmap_w1_bus_a16_v16;
return ERR_PTR(-ENOTSUPP);
}
struct regmap *__regmap_init_w1(struct device *w1_dev,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __regmap_init(w1_dev, bus, w1_dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_w1);
struct regmap *__devm_regmap_init_w1(struct device *w1_dev,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __devm_regmap_init(w1_dev, bus, w1_dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_w1);
MODULE_DESCRIPTION("Register map access API - W1 (1-Wire) support");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
+284
View File
@@ -0,0 +1,284 @@
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM regmap
#if !defined(_TRACE_REGMAP_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_REGMAP_H
#include <linux/ktime.h>
#include <linux/tracepoint.h>
#include "internal.h"
/*
* Log register events
*/
DECLARE_EVENT_CLASS(regmap_reg,
TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
TP_ARGS(map, reg, val),
TP_STRUCT__entry(
__string( name, regmap_name(map) )
__field( unsigned int, reg )
__field( unsigned int, val )
),
TP_fast_assign(
__assign_str(name);
__entry->reg = reg;
__entry->val = val;
),
TP_printk("%s reg=%x val=%x", __get_str(name), __entry->reg, __entry->val)
);
DEFINE_EVENT(regmap_reg, regmap_reg_write,
TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
TP_ARGS(map, reg, val)
);
DEFINE_EVENT(regmap_reg, regmap_reg_read,
TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
TP_ARGS(map, reg, val)
);
DEFINE_EVENT(regmap_reg, regmap_reg_read_cache,
TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
TP_ARGS(map, reg, val)
);
DECLARE_EVENT_CLASS(regmap_bulk,
TP_PROTO(struct regmap *map, unsigned int reg,
const void *val, int val_len),
TP_ARGS(map, reg, val, val_len),
TP_STRUCT__entry(
__string(name, regmap_name(map))
__field(unsigned int, reg)
__dynamic_array(char, buf, val_len)
__field(int, val_len)
),
TP_fast_assign(
__assign_str(name);
__entry->reg = reg;
__entry->val_len = val_len;
memcpy(__get_dynamic_array(buf), val, val_len);
),
TP_printk("%s reg=%x val=%s", __get_str(name), __entry->reg,
__print_hex(__get_dynamic_array(buf), __entry->val_len))
);
DEFINE_EVENT(regmap_bulk, regmap_bulk_write,
TP_PROTO(struct regmap *map, unsigned int reg,
const void *val, int val_len),
TP_ARGS(map, reg, val, val_len)
);
DEFINE_EVENT(regmap_bulk, regmap_bulk_read,
TP_PROTO(struct regmap *map, unsigned int reg,
const void *val, int val_len),
TP_ARGS(map, reg, val, val_len)
);
DECLARE_EVENT_CLASS(regmap_block,
TP_PROTO(struct regmap *map, unsigned int reg, int count),
TP_ARGS(map, reg, count),
TP_STRUCT__entry(
__string( name, regmap_name(map) )
__field( unsigned int, reg )
__field( int, count )
),
TP_fast_assign(
__assign_str(name);
__entry->reg = reg;
__entry->count = count;
),
TP_printk("%s reg=%x count=%d", __get_str(name), __entry->reg, __entry->count)
);
DEFINE_EVENT(regmap_block, regmap_hw_read_start,
TP_PROTO(struct regmap *map, unsigned int reg, int count),
TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_block, regmap_hw_read_done,
TP_PROTO(struct regmap *map, unsigned int reg, int count),
TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_block, regmap_hw_write_start,
TP_PROTO(struct regmap *map, unsigned int reg, int count),
TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_block, regmap_hw_write_done,
TP_PROTO(struct regmap *map, unsigned int reg, int count),
TP_ARGS(map, reg, count)
);
TRACE_EVENT(regcache_sync,
TP_PROTO(struct regmap *map, const char *type,
const char *status),
TP_ARGS(map, type, status),
TP_STRUCT__entry(
__string( name, regmap_name(map) )
__string( status, status )
__string( type, type )
),
TP_fast_assign(
__assign_str(name);
__assign_str(status);
__assign_str(type);
),
TP_printk("%s type=%s status=%s", __get_str(name),
__get_str(type), __get_str(status))
);
DECLARE_EVENT_CLASS(regmap_bool,
TP_PROTO(struct regmap *map, bool flag),
TP_ARGS(map, flag),
TP_STRUCT__entry(
__string( name, regmap_name(map) )
__field( int, flag )
),
TP_fast_assign(
__assign_str(name);
__entry->flag = flag;
),
TP_printk("%s flag=%d", __get_str(name), __entry->flag)
);
DEFINE_EVENT(regmap_bool, regmap_cache_only,
TP_PROTO(struct regmap *map, bool flag),
TP_ARGS(map, flag)
);
DEFINE_EVENT(regmap_bool, regmap_cache_bypass,
TP_PROTO(struct regmap *map, bool flag),
TP_ARGS(map, flag)
);
DECLARE_EVENT_CLASS(regmap_async,
TP_PROTO(struct regmap *map),
TP_ARGS(map),
TP_STRUCT__entry(
__string( name, regmap_name(map) )
),
TP_fast_assign(
__assign_str(name);
),
TP_printk("%s", __get_str(name))
);
DEFINE_EVENT(regmap_block, regmap_async_write_start,
TP_PROTO(struct regmap *map, unsigned int reg, int count),
TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_async, regmap_async_io_complete,
TP_PROTO(struct regmap *map),
TP_ARGS(map)
);
DEFINE_EVENT(regmap_async, regmap_async_complete_start,
TP_PROTO(struct regmap *map),
TP_ARGS(map)
);
DEFINE_EVENT(regmap_async, regmap_async_complete_done,
TP_PROTO(struct regmap *map),
TP_ARGS(map)
);
TRACE_EVENT(regcache_drop_region,
TP_PROTO(struct regmap *map, unsigned int from,
unsigned int to),
TP_ARGS(map, from, to),
TP_STRUCT__entry(
__string( name, regmap_name(map) )
__field( unsigned int, from )
__field( unsigned int, to )
),
TP_fast_assign(
__assign_str(name);
__entry->from = from;
__entry->to = to;
),
TP_printk("%s %u-%u", __get_str(name), __entry->from, __entry->to)
);
#endif /* _TRACE_REGMAP_H */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
/* This part must be outside protection */
#include <trace/define_trace.h>