mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-07 03:18:31 +00:00
Merge branch 'for-linus' of git://opensource.wolfsonmicro.com/regmap
* 'for-linus' of git://opensource.wolfsonmicro.com/regmap: (62 commits) mfd: Enable rbtree cache for wm831x devices regmap: Support some block operations on cached devices regmap: Allow caches for devices with no defaults regmap: Ensure rbtree syncs registers set to zero properly regmap: Allow rbtree to cache zero default values regmap: Warn on raw I/O as well as bulk reads that bypass cache regmap: Return a sensible error code if we fail to read the cache regmap: Use bsearch() to search the register defaults regmap: Fix doc comment regmap: Optimize the lookup path to use binary search regmap: Ensure we scream if we enable cache bypass/only at the same time regmap: Implement regcache_cache_bypass helper function regmap: Save/restore the bypass state upon syncing regmap: Lock the sync path, ensure we use the lockless _regmap_write() regmap: Fix apostrophe usage regmap: Make _regmap_write() global regmap: Fix lock used for regcache_cache_only() regmap: Grab the lock in regcache_cache_only() regmap: Modify map->cache_bypass directly regmap: Fix regcache_sync generic implementation ...
This commit is contained in:
commit
4e7e2a2008
23 changed files with 2420 additions and 566 deletions
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
config REGMAP
|
config REGMAP
|
||||||
default y if (REGMAP_I2C || REGMAP_SPI)
|
default y if (REGMAP_I2C || REGMAP_SPI)
|
||||||
|
select LZO_COMPRESS
|
||||||
|
select LZO_DECOMPRESS
|
||||||
bool
|
bool
|
||||||
|
|
||||||
config REGMAP_I2C
|
config REGMAP_I2C
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
obj-$(CONFIG_REGMAP) += regmap.o
|
obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o regcache-lzo.o
|
||||||
|
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
|
||||||
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
|
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
|
||||||
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
|
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
|
||||||
|
|
128
drivers/base/regmap/internal.h
Normal file
128
drivers/base/regmap/internal.h
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Register map access API internal header
|
||||||
|
*
|
||||||
|
* Copyright 2011 Wolfson Microelectronics plc
|
||||||
|
*
|
||||||
|
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _REGMAP_INTERNAL_H
|
||||||
|
#define _REGMAP_INTERNAL_H
|
||||||
|
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
|
||||||
|
struct regmap;
|
||||||
|
struct regcache_ops;
|
||||||
|
|
||||||
|
struct regmap_format {
|
||||||
|
size_t buf_size;
|
||||||
|
size_t reg_bytes;
|
||||||
|
size_t val_bytes;
|
||||||
|
void (*format_write)(struct regmap *map,
|
||||||
|
unsigned int reg, unsigned int val);
|
||||||
|
void (*format_reg)(void *buf, unsigned int reg);
|
||||||
|
void (*format_val)(void *buf, unsigned int val);
|
||||||
|
unsigned int (*parse_val)(void *buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct regmap {
|
||||||
|
struct mutex lock;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
struct dentry *debugfs;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
unsigned int max_register;
|
||||||
|
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);
|
||||||
|
|
||||||
|
u8 read_flag_mask;
|
||||||
|
u8 write_flag_mask;
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
unsigned int cache_only:1;
|
||||||
|
/* if set, only the HW is modified not the cache */
|
||||||
|
unsigned int cache_bypass:1;
|
||||||
|
/* if set, remember to free reg_defaults_raw */
|
||||||
|
unsigned int cache_free:1;
|
||||||
|
|
||||||
|
struct reg_default *reg_defaults;
|
||||||
|
const void *reg_defaults_raw;
|
||||||
|
void *cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct regcache_ops {
|
||||||
|
const char *name;
|
||||||
|
enum regcache_type type;
|
||||||
|
int (*init)(struct regmap *map);
|
||||||
|
int (*exit)(struct regmap *map);
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
int _regmap_write(struct regmap *map, unsigned int reg,
|
||||||
|
unsigned int val);
|
||||||
|
|
||||||
|
#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);
|
||||||
|
#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) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* regcache core declarations */
|
||||||
|
int regcache_init(struct regmap *map);
|
||||||
|
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);
|
||||||
|
|
||||||
|
unsigned int regcache_get_val(const void *base, unsigned int idx,
|
||||||
|
unsigned int word_size);
|
||||||
|
bool regcache_set_val(void *base, unsigned int idx,
|
||||||
|
unsigned int val, unsigned int word_size);
|
||||||
|
int regcache_lookup_reg(struct regmap *map, unsigned int reg);
|
||||||
|
int regcache_insert_reg(struct regmap *map, unsigned int reg,
|
||||||
|
unsigned int val);
|
||||||
|
|
||||||
|
extern struct regcache_ops regcache_indexed_ops;
|
||||||
|
extern struct regcache_ops regcache_rbtree_ops;
|
||||||
|
extern struct regcache_ops regcache_lzo_ops;
|
||||||
|
|
||||||
|
#endif
|
64
drivers/base/regmap/regcache-indexed.c
Normal file
64
drivers/base/regmap/regcache-indexed.c
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Register cache access API - indexed caching support
|
||||||
|
*
|
||||||
|
* Copyright 2011 Wolfson Microelectronics plc
|
||||||
|
*
|
||||||
|
* Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
static int regcache_indexed_read(struct regmap *map, unsigned int reg,
|
||||||
|
unsigned int *value)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regcache_lookup_reg(map, reg);
|
||||||
|
if (ret >= 0)
|
||||||
|
*value = map->reg_defaults[ret].def;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_indexed_write(struct regmap *map, unsigned int reg,
|
||||||
|
unsigned int value)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regcache_lookup_reg(map, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return regcache_insert_reg(map, reg, value);
|
||||||
|
map->reg_defaults[ret].def = value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_indexed_sync(struct regmap *map)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
for (i = 0; i < map->num_reg_defaults; i++) {
|
||||||
|
ret = _regmap_write(map, map->reg_defaults[i].reg,
|
||||||
|
map->reg_defaults[i].def);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
|
||||||
|
map->reg_defaults[i].reg,
|
||||||
|
map->reg_defaults[i].def);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct regcache_ops regcache_indexed_ops = {
|
||||||
|
.type = REGCACHE_INDEXED,
|
||||||
|
.name = "indexed",
|
||||||
|
.read = regcache_indexed_read,
|
||||||
|
.write = regcache_indexed_write,
|
||||||
|
.sync = regcache_indexed_sync
|
||||||
|
};
|
361
drivers/base/regmap/regcache-lzo.c
Normal file
361
drivers/base/regmap/regcache-lzo.c
Normal file
|
@ -0,0 +1,361 @@
|
||||||
|
/*
|
||||||
|
* Register cache access API - LZO caching support
|
||||||
|
*
|
||||||
|
* Copyright 2011 Wolfson Microelectronics plc
|
||||||
|
*
|
||||||
|
* Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/lzo.h>
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
struct regcache_lzo_ctx {
|
||||||
|
void *wmem;
|
||||||
|
void *dst;
|
||||||
|
const void *src;
|
||||||
|
size_t src_len;
|
||||||
|
size_t dst_len;
|
||||||
|
size_t decompressed_size;
|
||||||
|
unsigned long *sync_bmp;
|
||||||
|
int sync_bmp_nbits;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LZO_BLOCK_NUM 8
|
||||||
|
static int regcache_lzo_block_count(void)
|
||||||
|
{
|
||||||
|
return LZO_BLOCK_NUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_lzo_prepare(struct regcache_lzo_ctx *lzo_ctx)
|
||||||
|
{
|
||||||
|
lzo_ctx->wmem = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
|
||||||
|
if (!lzo_ctx->wmem)
|
||||||
|
return -ENOMEM;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_lzo_compress(struct regcache_lzo_ctx *lzo_ctx)
|
||||||
|
{
|
||||||
|
size_t compress_size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = lzo1x_1_compress(lzo_ctx->src, lzo_ctx->src_len,
|
||||||
|
lzo_ctx->dst, &compress_size, lzo_ctx->wmem);
|
||||||
|
if (ret != LZO_E_OK || compress_size > lzo_ctx->dst_len)
|
||||||
|
return -EINVAL;
|
||||||
|
lzo_ctx->dst_len = compress_size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_lzo_decompress(struct regcache_lzo_ctx *lzo_ctx)
|
||||||
|
{
|
||||||
|
size_t dst_len;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dst_len = lzo_ctx->dst_len;
|
||||||
|
ret = lzo1x_decompress_safe(lzo_ctx->src, lzo_ctx->src_len,
|
||||||
|
lzo_ctx->dst, &dst_len);
|
||||||
|
if (ret != LZO_E_OK || dst_len != lzo_ctx->dst_len)
|
||||||
|
return -EINVAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_lzo_compress_cache_block(struct regmap *map,
|
||||||
|
struct regcache_lzo_ctx *lzo_ctx)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
lzo_ctx->dst_len = lzo1x_worst_compress(PAGE_SIZE);
|
||||||
|
lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
|
||||||
|
if (!lzo_ctx->dst) {
|
||||||
|
lzo_ctx->dst_len = 0;
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regcache_lzo_compress(lzo_ctx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_lzo_decompress_cache_block(struct regmap *map,
|
||||||
|
struct regcache_lzo_ctx *lzo_ctx)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
lzo_ctx->dst_len = lzo_ctx->decompressed_size;
|
||||||
|
lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
|
||||||
|
if (!lzo_ctx->dst) {
|
||||||
|
lzo_ctx->dst_len = 0;
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regcache_lzo_decompress(lzo_ctx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int regcache_lzo_get_blkindex(struct regmap *map,
|
||||||
|
unsigned int reg)
|
||||||
|
{
|
||||||
|
return (reg * map->cache_word_size) /
|
||||||
|
DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int regcache_lzo_get_blkpos(struct regmap *map,
|
||||||
|
unsigned int reg)
|
||||||
|
{
|
||||||
|
return reg % (DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count()) /
|
||||||
|
map->cache_word_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int regcache_lzo_get_blksize(struct regmap *map)
|
||||||
|
{
|
||||||
|
return DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_lzo_init(struct regmap *map)
|
||||||
|
{
|
||||||
|
struct regcache_lzo_ctx **lzo_blocks;
|
||||||
|
size_t bmp_size;
|
||||||
|
int ret, i, blksize, blkcount;
|
||||||
|
const char *p, *end;
|
||||||
|
unsigned long *sync_bmp;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
blkcount = regcache_lzo_block_count();
|
||||||
|
map->cache = kzalloc(blkcount * sizeof *lzo_blocks,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!map->cache)
|
||||||
|
return -ENOMEM;
|
||||||
|
lzo_blocks = map->cache;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* allocate a bitmap to be used when syncing the cache with
|
||||||
|
* the hardware. Each time a register is modified, the corresponding
|
||||||
|
* bit is set in the bitmap, so we know that we have to sync
|
||||||
|
* that register.
|
||||||
|
*/
|
||||||
|
bmp_size = map->num_reg_defaults_raw;
|
||||||
|
sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof(long),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!sync_bmp) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
bitmap_zero(sync_bmp, bmp_size);
|
||||||
|
|
||||||
|
/* allocate the lzo blocks and initialize them */
|
||||||
|
for (i = 0; i < blkcount; i++) {
|
||||||
|
lzo_blocks[i] = kzalloc(sizeof **lzo_blocks,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!lzo_blocks[i]) {
|
||||||
|
kfree(sync_bmp);
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
lzo_blocks[i]->sync_bmp = sync_bmp;
|
||||||
|
lzo_blocks[i]->sync_bmp_nbits = bmp_size;
|
||||||
|
/* alloc the working space for the compressed block */
|
||||||
|
ret = regcache_lzo_prepare(lzo_blocks[i]);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
blksize = regcache_lzo_get_blksize(map);
|
||||||
|
p = map->reg_defaults_raw;
|
||||||
|
end = map->reg_defaults_raw + map->cache_size_raw;
|
||||||
|
/* compress the register map and fill the lzo blocks */
|
||||||
|
for (i = 0; i < blkcount; i++, p += blksize) {
|
||||||
|
lzo_blocks[i]->src = p;
|
||||||
|
if (p + blksize > end)
|
||||||
|
lzo_blocks[i]->src_len = end - p;
|
||||||
|
else
|
||||||
|
lzo_blocks[i]->src_len = blksize;
|
||||||
|
ret = regcache_lzo_compress_cache_block(map,
|
||||||
|
lzo_blocks[i]);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err;
|
||||||
|
lzo_blocks[i]->decompressed_size =
|
||||||
|
lzo_blocks[i]->src_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
regcache_exit(map);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_lzo_exit(struct regmap *map)
|
||||||
|
{
|
||||||
|
struct regcache_lzo_ctx **lzo_blocks;
|
||||||
|
int i, blkcount;
|
||||||
|
|
||||||
|
lzo_blocks = map->cache;
|
||||||
|
if (!lzo_blocks)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
blkcount = regcache_lzo_block_count();
|
||||||
|
/*
|
||||||
|
* the pointer to the bitmap used for syncing the cache
|
||||||
|
* is shared amongst all lzo_blocks. Ensure it is freed
|
||||||
|
* only once.
|
||||||
|
*/
|
||||||
|
if (lzo_blocks[0])
|
||||||
|
kfree(lzo_blocks[0]->sync_bmp);
|
||||||
|
for (i = 0; i < blkcount; i++) {
|
||||||
|
if (lzo_blocks[i]) {
|
||||||
|
kfree(lzo_blocks[i]->wmem);
|
||||||
|
kfree(lzo_blocks[i]->dst);
|
||||||
|
}
|
||||||
|
/* each lzo_block is a pointer returned by kmalloc or NULL */
|
||||||
|
kfree(lzo_blocks[i]);
|
||||||
|
}
|
||||||
|
kfree(lzo_blocks);
|
||||||
|
map->cache = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_lzo_read(struct regmap *map,
|
||||||
|
unsigned int reg, unsigned int *value)
|
||||||
|
{
|
||||||
|
struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
|
||||||
|
int ret, blkindex, blkpos;
|
||||||
|
size_t blksize, tmp_dst_len;
|
||||||
|
void *tmp_dst;
|
||||||
|
|
||||||
|
/* index of the compressed lzo block */
|
||||||
|
blkindex = regcache_lzo_get_blkindex(map, reg);
|
||||||
|
/* register index within the decompressed block */
|
||||||
|
blkpos = regcache_lzo_get_blkpos(map, reg);
|
||||||
|
/* size of the compressed block */
|
||||||
|
blksize = regcache_lzo_get_blksize(map);
|
||||||
|
lzo_blocks = map->cache;
|
||||||
|
lzo_block = lzo_blocks[blkindex];
|
||||||
|
|
||||||
|
/* save the pointer and length of the compressed block */
|
||||||
|
tmp_dst = lzo_block->dst;
|
||||||
|
tmp_dst_len = lzo_block->dst_len;
|
||||||
|
|
||||||
|
/* prepare the source to be the compressed block */
|
||||||
|
lzo_block->src = lzo_block->dst;
|
||||||
|
lzo_block->src_len = lzo_block->dst_len;
|
||||||
|
|
||||||
|
/* decompress the block */
|
||||||
|
ret = regcache_lzo_decompress_cache_block(map, lzo_block);
|
||||||
|
if (ret >= 0)
|
||||||
|
/* fetch the value from the cache */
|
||||||
|
*value = regcache_get_val(lzo_block->dst, blkpos,
|
||||||
|
map->cache_word_size);
|
||||||
|
|
||||||
|
kfree(lzo_block->dst);
|
||||||
|
/* restore the pointer and length of the compressed block */
|
||||||
|
lzo_block->dst = tmp_dst;
|
||||||
|
lzo_block->dst_len = tmp_dst_len;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_lzo_write(struct regmap *map,
|
||||||
|
unsigned int reg, unsigned int value)
|
||||||
|
{
|
||||||
|
struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
|
||||||
|
int ret, blkindex, blkpos;
|
||||||
|
size_t blksize, tmp_dst_len;
|
||||||
|
void *tmp_dst;
|
||||||
|
|
||||||
|
/* index of the compressed lzo block */
|
||||||
|
blkindex = regcache_lzo_get_blkindex(map, reg);
|
||||||
|
/* register index within the decompressed block */
|
||||||
|
blkpos = regcache_lzo_get_blkpos(map, reg);
|
||||||
|
/* size of the compressed block */
|
||||||
|
blksize = regcache_lzo_get_blksize(map);
|
||||||
|
lzo_blocks = map->cache;
|
||||||
|
lzo_block = lzo_blocks[blkindex];
|
||||||
|
|
||||||
|
/* save the pointer and length of the compressed block */
|
||||||
|
tmp_dst = lzo_block->dst;
|
||||||
|
tmp_dst_len = lzo_block->dst_len;
|
||||||
|
|
||||||
|
/* prepare the source to be the compressed block */
|
||||||
|
lzo_block->src = lzo_block->dst;
|
||||||
|
lzo_block->src_len = lzo_block->dst_len;
|
||||||
|
|
||||||
|
/* decompress the block */
|
||||||
|
ret = regcache_lzo_decompress_cache_block(map, lzo_block);
|
||||||
|
if (ret < 0) {
|
||||||
|
kfree(lzo_block->dst);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write the new value to the cache */
|
||||||
|
if (regcache_set_val(lzo_block->dst, blkpos, value,
|
||||||
|
map->cache_word_size)) {
|
||||||
|
kfree(lzo_block->dst);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prepare the source to be the decompressed block */
|
||||||
|
lzo_block->src = lzo_block->dst;
|
||||||
|
lzo_block->src_len = lzo_block->dst_len;
|
||||||
|
|
||||||
|
/* compress the block */
|
||||||
|
ret = regcache_lzo_compress_cache_block(map, lzo_block);
|
||||||
|
if (ret < 0) {
|
||||||
|
kfree(lzo_block->dst);
|
||||||
|
kfree(lzo_block->src);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set the bit so we know we have to sync this register */
|
||||||
|
set_bit(reg, lzo_block->sync_bmp);
|
||||||
|
kfree(tmp_dst);
|
||||||
|
kfree(lzo_block->src);
|
||||||
|
return 0;
|
||||||
|
out:
|
||||||
|
lzo_block->dst = tmp_dst;
|
||||||
|
lzo_block->dst_len = tmp_dst_len;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_lzo_sync(struct regmap *map)
|
||||||
|
{
|
||||||
|
struct regcache_lzo_ctx **lzo_blocks;
|
||||||
|
unsigned int val;
|
||||||
|
int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
lzo_blocks = map->cache;
|
||||||
|
for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) {
|
||||||
|
ret = regcache_read(map, i, &val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
map->cache_bypass = 1;
|
||||||
|
ret = _regmap_write(map, i, val);
|
||||||
|
map->cache_bypass = 0;
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
|
||||||
|
i, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct regcache_ops regcache_lzo_ops = {
|
||||||
|
.type = REGCACHE_LZO,
|
||||||
|
.name = "lzo",
|
||||||
|
.init = regcache_lzo_init,
|
||||||
|
.exit = regcache_lzo_exit,
|
||||||
|
.read = regcache_lzo_read,
|
||||||
|
.write = regcache_lzo_write,
|
||||||
|
.sync = regcache_lzo_sync
|
||||||
|
};
|
345
drivers/base/regmap/regcache-rbtree.c
Normal file
345
drivers/base/regmap/regcache-rbtree.c
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
/*
|
||||||
|
* Register cache access API - rbtree caching support
|
||||||
|
*
|
||||||
|
* Copyright 2011 Wolfson Microelectronics plc
|
||||||
|
*
|
||||||
|
* Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/rbtree.h>
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
|
||||||
|
unsigned int value);
|
||||||
|
|
||||||
|
struct regcache_rbtree_node {
|
||||||
|
/* the actual rbtree node holding this block */
|
||||||
|
struct rb_node node;
|
||||||
|
/* base register handled by this block */
|
||||||
|
unsigned int base_reg;
|
||||||
|
/* block of adjacent registers */
|
||||||
|
void *block;
|
||||||
|
/* number of registers available in the block */
|
||||||
|
unsigned int blklen;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct regcache_rbtree_ctx {
|
||||||
|
struct rb_root root;
|
||||||
|
struct regcache_rbtree_node *cached_rbnode;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void regcache_rbtree_get_base_top_reg(
|
||||||
|
struct regcache_rbtree_node *rbnode,
|
||||||
|
unsigned int *base, unsigned int *top)
|
||||||
|
{
|
||||||
|
*base = rbnode->base_reg;
|
||||||
|
*top = rbnode->base_reg + rbnode->blklen - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int regcache_rbtree_get_register(
|
||||||
|
struct regcache_rbtree_node *rbnode, unsigned int idx,
|
||||||
|
unsigned int word_size)
|
||||||
|
{
|
||||||
|
return regcache_get_val(rbnode->block, idx, word_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void regcache_rbtree_set_register(struct regcache_rbtree_node *rbnode,
|
||||||
|
unsigned int idx, unsigned int val,
|
||||||
|
unsigned int word_size)
|
||||||
|
{
|
||||||
|
regcache_set_val(rbnode->block, idx, val, word_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(rbnode, &base_reg, &top_reg);
|
||||||
|
if (reg >= base_reg && reg <= top_reg)
|
||||||
|
return rbnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = rbtree_ctx->root.rb_node;
|
||||||
|
while (node) {
|
||||||
|
rbnode = container_of(node, struct regcache_rbtree_node, node);
|
||||||
|
regcache_rbtree_get_base_top_reg(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 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 = container_of(*new, struct regcache_rbtree_node,
|
||||||
|
node);
|
||||||
|
/* base and top registers of the current rbnode */
|
||||||
|
regcache_rbtree_get_base_top_reg(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_rbtree_init(struct regmap *map)
|
||||||
|
{
|
||||||
|
struct regcache_rbtree_ctx *rbtree_ctx;
|
||||||
|
int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
map->cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
|
||||||
|
if (!map->cache)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rbtree_ctx = map->cache;
|
||||||
|
rbtree_ctx->root = RB_ROOT;
|
||||||
|
rbtree_ctx->cached_rbnode = NULL;
|
||||||
|
|
||||||
|
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)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
regcache_exit(map);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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->block);
|
||||||
|
kfree(rbtree_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* release the resources */
|
||||||
|
kfree(map->cache);
|
||||||
|
map->cache = NULL;
|
||||||
|
|
||||||
|
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;
|
||||||
|
*value = regcache_rbtree_get_register(rbnode, reg_tmp,
|
||||||
|
map->cache_word_size);
|
||||||
|
} else {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int regcache_rbtree_insert_to_block(struct regcache_rbtree_node *rbnode,
|
||||||
|
unsigned int pos, unsigned int reg,
|
||||||
|
unsigned int value, unsigned int word_size)
|
||||||
|
{
|
||||||
|
u8 *blk;
|
||||||
|
|
||||||
|
blk = krealloc(rbnode->block,
|
||||||
|
(rbnode->blklen + 1) * word_size, GFP_KERNEL);
|
||||||
|
if (!blk)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* insert the register value in the correct place in the rbnode block */
|
||||||
|
memmove(blk + (pos + 1) * word_size,
|
||||||
|
blk + pos * word_size,
|
||||||
|
(rbnode->blklen - pos) * word_size);
|
||||||
|
|
||||||
|
/* update the rbnode block, its size and the base register */
|
||||||
|
rbnode->block = blk;
|
||||||
|
rbnode->blklen++;
|
||||||
|
if (!pos)
|
||||||
|
rbnode->base_reg = reg;
|
||||||
|
|
||||||
|
regcache_rbtree_set_register(rbnode, pos, value, word_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 val;
|
||||||
|
unsigned int reg_tmp;
|
||||||
|
unsigned int pos;
|
||||||
|
int i;
|
||||||
|
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;
|
||||||
|
val = regcache_rbtree_get_register(rbnode, reg_tmp,
|
||||||
|
map->cache_word_size);
|
||||||
|
if (val == value)
|
||||||
|
return 0;
|
||||||
|
regcache_rbtree_set_register(rbnode, reg_tmp, value,
|
||||||
|
map->cache_word_size);
|
||||||
|
} else {
|
||||||
|
/* look for an adjacent register to the one we are about to add */
|
||||||
|
for (node = rb_first(&rbtree_ctx->root); node;
|
||||||
|
node = rb_next(node)) {
|
||||||
|
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node);
|
||||||
|
for (i = 0; i < rbnode_tmp->blklen; i++) {
|
||||||
|
reg_tmp = rbnode_tmp->base_reg + i;
|
||||||
|
if (abs(reg_tmp - reg) != 1)
|
||||||
|
continue;
|
||||||
|
/* decide where in the block to place our register */
|
||||||
|
if (reg_tmp + 1 == reg)
|
||||||
|
pos = i + 1;
|
||||||
|
else
|
||||||
|
pos = i;
|
||||||
|
ret = regcache_rbtree_insert_to_block(rbnode_tmp, pos,
|
||||||
|
reg, value,
|
||||||
|
map->cache_word_size);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
rbtree_ctx->cached_rbnode = rbnode_tmp;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* we did not manage to find a place to insert it in an existing
|
||||||
|
* block so create a new rbnode with a single register in its block.
|
||||||
|
* This block will get populated further if any other adjacent
|
||||||
|
* registers get modified in the future.
|
||||||
|
*/
|
||||||
|
rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL);
|
||||||
|
if (!rbnode)
|
||||||
|
return -ENOMEM;
|
||||||
|
rbnode->blklen = 1;
|
||||||
|
rbnode->base_reg = reg;
|
||||||
|
rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!rbnode->block) {
|
||||||
|
kfree(rbnode);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size);
|
||||||
|
regcache_rbtree_insert(&rbtree_ctx->root, rbnode);
|
||||||
|
rbtree_ctx->cached_rbnode = rbnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_rbtree_sync(struct regmap *map)
|
||||||
|
{
|
||||||
|
struct regcache_rbtree_ctx *rbtree_ctx;
|
||||||
|
struct rb_node *node;
|
||||||
|
struct regcache_rbtree_node *rbnode;
|
||||||
|
unsigned int regtmp;
|
||||||
|
unsigned int val;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
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);
|
||||||
|
for (i = 0; i < rbnode->blklen; i++) {
|
||||||
|
regtmp = rbnode->base_reg + i;
|
||||||
|
val = regcache_rbtree_get_register(rbnode, i,
|
||||||
|
map->cache_word_size);
|
||||||
|
|
||||||
|
/* Is this the hardware default? If so skip. */
|
||||||
|
ret = regcache_lookup_reg(map, i);
|
||||||
|
if (ret > 0 && val == map->reg_defaults[ret].def)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
map->cache_bypass = 1;
|
||||||
|
ret = _regmap_write(map, regtmp, val);
|
||||||
|
map->cache_bypass = 0;
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
|
||||||
|
regtmp, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct regcache_ops regcache_rbtree_ops = {
|
||||||
|
.type = REGCACHE_RBTREE,
|
||||||
|
.name = "rbtree",
|
||||||
|
.init = regcache_rbtree_init,
|
||||||
|
.exit = regcache_rbtree_exit,
|
||||||
|
.read = regcache_rbtree_read,
|
||||||
|
.write = regcache_rbtree_write,
|
||||||
|
.sync = regcache_rbtree_sync
|
||||||
|
};
|
401
drivers/base/regmap/regcache.c
Normal file
401
drivers/base/regmap/regcache.c
Normal file
|
@ -0,0 +1,401 @@
|
||||||
|
/*
|
||||||
|
* Register cache access API
|
||||||
|
*
|
||||||
|
* Copyright 2011 Wolfson Microelectronics plc
|
||||||
|
*
|
||||||
|
* Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <trace/events/regmap.h>
|
||||||
|
#include <linux/bsearch.h>
|
||||||
|
#include <linux/sort.h>
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
static const struct regcache_ops *cache_types[] = {
|
||||||
|
®cache_indexed_ops,
|
||||||
|
®cache_rbtree_ops,
|
||||||
|
®cache_lzo_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int regcache_hw_init(struct regmap *map)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
int ret;
|
||||||
|
int count;
|
||||||
|
unsigned int val;
|
||||||
|
void *tmp_buf;
|
||||||
|
|
||||||
|
if (!map->num_reg_defaults_raw)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!map->reg_defaults_raw) {
|
||||||
|
dev_warn(map->dev, "No cache defaults, reading back from HW\n");
|
||||||
|
tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL);
|
||||||
|
if (!tmp_buf)
|
||||||
|
return -EINVAL;
|
||||||
|
ret = regmap_bulk_read(map, 0, tmp_buf,
|
||||||
|
map->num_reg_defaults_raw);
|
||||||
|
if (ret < 0) {
|
||||||
|
kfree(tmp_buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
map->reg_defaults_raw = tmp_buf;
|
||||||
|
map->cache_free = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calculate the size of reg_defaults */
|
||||||
|
for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) {
|
||||||
|
val = regcache_get_val(map->reg_defaults_raw,
|
||||||
|
i, map->cache_word_size);
|
||||||
|
if (!val)
|
||||||
|
continue;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
map->reg_defaults = kmalloc(count * sizeof(struct reg_default),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!map->reg_defaults)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* fill the reg_defaults */
|
||||||
|
map->num_reg_defaults = count;
|
||||||
|
for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
|
||||||
|
val = regcache_get_val(map->reg_defaults_raw,
|
||||||
|
i, map->cache_word_size);
|
||||||
|
if (!val)
|
||||||
|
continue;
|
||||||
|
map->reg_defaults[j].reg = i;
|
||||||
|
map->reg_defaults[j].def = val;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int regcache_init(struct regmap *map)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
void *tmp_buf;
|
||||||
|
|
||||||
|
if (map->cache_type == REGCACHE_NONE) {
|
||||||
|
map->cache_bypass = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 compress type: %d\n",
|
||||||
|
map->cache_type);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (map->reg_defaults) {
|
||||||
|
if (!map->num_reg_defaults)
|
||||||
|
return -EINVAL;
|
||||||
|
tmp_buf = kmemdup(map->reg_defaults, map->num_reg_defaults *
|
||||||
|
sizeof(struct reg_default), GFP_KERNEL);
|
||||||
|
if (!tmp_buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
map->reg_defaults = tmp_buf;
|
||||||
|
} else if (map->num_reg_defaults_raw) {
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
ret = regcache_hw_init(map);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!map->max_register)
|
||||||
|
map->max_register = map->num_reg_defaults_raw;
|
||||||
|
|
||||||
|
if (map->cache_ops->init) {
|
||||||
|
dev_dbg(map->dev, "Initializing %s cache\n",
|
||||||
|
map->cache_ops->name);
|
||||||
|
return map->cache_ops->init(map);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void regcache_exit(struct regmap *map)
|
||||||
|
{
|
||||||
|
if (map->cache_type == REGCACHE_NONE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BUG_ON(!map->cache_ops);
|
||||||
|
|
||||||
|
kfree(map->reg_defaults);
|
||||||
|
if (map->cache_free)
|
||||||
|
kfree(map->reg_defaults_raw);
|
||||||
|
|
||||||
|
if (map->cache_ops->exit) {
|
||||||
|
dev_dbg(map->dev, "Destroying %s cache\n",
|
||||||
|
map->cache_ops->name);
|
||||||
|
map->cache_ops->exit(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
if (map->cache_type == REGCACHE_NONE)
|
||||||
|
return -ENOSYS;
|
||||||
|
|
||||||
|
BUG_ON(!map->cache_ops);
|
||||||
|
|
||||||
|
if (!regmap_readable(map, reg))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (!regmap_volatile(map, reg))
|
||||||
|
return map->cache_ops->read(map, reg, value);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(regcache_read);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_writeable(map, reg))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (!regmap_volatile(map, reg))
|
||||||
|
return map->cache_ops->write(map, reg, value);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(regcache_write);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 val;
|
||||||
|
unsigned int i;
|
||||||
|
const char *name;
|
||||||
|
unsigned int bypass;
|
||||||
|
|
||||||
|
BUG_ON(!map->cache_ops);
|
||||||
|
|
||||||
|
mutex_lock(&map->lock);
|
||||||
|
/* 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->dev, name, "start");
|
||||||
|
if (map->cache_ops->sync) {
|
||||||
|
ret = map->cache_ops->sync(map);
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < map->num_reg_defaults; i++) {
|
||||||
|
ret = regcache_read(map, i, &val);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
map->cache_bypass = 1;
|
||||||
|
ret = _regmap_write(map, i, val);
|
||||||
|
map->cache_bypass = 0;
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
|
||||||
|
map->reg_defaults[i].reg,
|
||||||
|
map->reg_defaults[i].def);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
trace_regcache_sync(map->dev, name, "stop");
|
||||||
|
/* Restore the bypass state */
|
||||||
|
map->cache_bypass = bypass;
|
||||||
|
mutex_unlock(&map->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(regcache_sync);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* regcache_cache_only: Put a register map into cache only mode
|
||||||
|
*
|
||||||
|
* @map: map to configure
|
||||||
|
* @cache_only: 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)
|
||||||
|
{
|
||||||
|
mutex_lock(&map->lock);
|
||||||
|
WARN_ON(map->cache_bypass && enable);
|
||||||
|
map->cache_only = enable;
|
||||||
|
mutex_unlock(&map->lock);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(regcache_cache_only);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* regcache_cache_bypass: Put a register map into cache bypass mode
|
||||||
|
*
|
||||||
|
* @map: map to configure
|
||||||
|
* @cache_bypass: flag if changes should not be written to the hardware
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* the cache directly. This is useful when syncing the cache back to
|
||||||
|
* the hardware.
|
||||||
|
*/
|
||||||
|
void regcache_cache_bypass(struct regmap *map, bool enable)
|
||||||
|
{
|
||||||
|
mutex_lock(&map->lock);
|
||||||
|
WARN_ON(map->cache_only && enable);
|
||||||
|
map->cache_bypass = enable;
|
||||||
|
mutex_unlock(&map->lock);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
|
||||||
|
|
||||||
|
bool regcache_set_val(void *base, unsigned int idx,
|
||||||
|
unsigned int val, unsigned int word_size)
|
||||||
|
{
|
||||||
|
switch (word_size) {
|
||||||
|
case 1: {
|
||||||
|
u8 *cache = base;
|
||||||
|
if (cache[idx] == val)
|
||||||
|
return true;
|
||||||
|
cache[idx] = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
u16 *cache = base;
|
||||||
|
if (cache[idx] == val)
|
||||||
|
return true;
|
||||||
|
cache[idx] = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
BUG();
|
||||||
|
}
|
||||||
|
/* unreachable */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int regcache_get_val(const void *base, unsigned int idx,
|
||||||
|
unsigned int word_size)
|
||||||
|
{
|
||||||
|
if (!base)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
switch (word_size) {
|
||||||
|
case 1: {
|
||||||
|
const u8 *cache = base;
|
||||||
|
return cache[idx];
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
const u16 *cache = base;
|
||||||
|
return cache[idx];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
BUG();
|
||||||
|
}
|
||||||
|
/* unreachable */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regcache_default_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct reg_default *_a = a;
|
||||||
|
const struct reg_default *_b = b;
|
||||||
|
|
||||||
|
return _a->reg - _b->reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_default_cmp);
|
||||||
|
|
||||||
|
if (r)
|
||||||
|
return r - map->reg_defaults;
|
||||||
|
else
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int regcache_insert_reg(struct regmap *map, unsigned int reg,
|
||||||
|
unsigned int val)
|
||||||
|
{
|
||||||
|
void *tmp;
|
||||||
|
|
||||||
|
tmp = krealloc(map->reg_defaults,
|
||||||
|
(map->num_reg_defaults + 1) * sizeof(struct reg_default),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!tmp)
|
||||||
|
return -ENOMEM;
|
||||||
|
map->reg_defaults = tmp;
|
||||||
|
map->num_reg_defaults++;
|
||||||
|
map->reg_defaults[map->num_reg_defaults - 1].reg = reg;
|
||||||
|
map->reg_defaults[map->num_reg_defaults - 1].def = val;
|
||||||
|
sort(map->reg_defaults, map->num_reg_defaults,
|
||||||
|
sizeof(struct reg_default), regcache_default_cmp, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
209
drivers/base/regmap/regmap-debugfs.c
Normal file
209
drivers/base/regmap/regmap-debugfs.c
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* Register map access API - debugfs
|
||||||
|
*
|
||||||
|
* Copyright 2011 Wolfson Microelectronics plc
|
||||||
|
*
|
||||||
|
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/debugfs.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
static struct dentry *regmap_debugfs_root;
|
||||||
|
|
||||||
|
/* Calculate the length of a fixed format */
|
||||||
|
static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size)
|
||||||
|
{
|
||||||
|
snprintf(buf, buf_size, "%x", max_val);
|
||||||
|
return strlen(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regmap_open_file(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
file->private_data = inode->i_private;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
|
{
|
||||||
|
int reg_len, val_len, tot_len;
|
||||||
|
size_t buf_pos = 0;
|
||||||
|
loff_t p = 0;
|
||||||
|
ssize_t ret;
|
||||||
|
int i;
|
||||||
|
struct regmap *map = file->private_data;
|
||||||
|
char *buf;
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
if (*ppos < 0 || !count)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
buf = kmalloc(count, GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Calculate the length of a fixed format */
|
||||||
|
reg_len = regmap_calc_reg_len(map->max_register, buf, count);
|
||||||
|
val_len = 2 * map->format.val_bytes;
|
||||||
|
tot_len = reg_len + val_len + 3; /* : \n */
|
||||||
|
|
||||||
|
for (i = 0; i < map->max_register + 1; i++) {
|
||||||
|
if (!regmap_readable(map, i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (regmap_precious(map, i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* If we're in the region the user is trying to read */
|
||||||
|
if (p >= *ppos) {
|
||||||
|
/* ...but not beyond it */
|
||||||
|
if (buf_pos >= count - 1 - tot_len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Format the register */
|
||||||
|
snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
|
||||||
|
reg_len, i);
|
||||||
|
buf_pos += 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", val_len, val);
|
||||||
|
else
|
||||||
|
memset(buf + buf_pos, 'X', val_len);
|
||||||
|
buf_pos += 2 * map->format.val_bytes;
|
||||||
|
|
||||||
|
buf[buf_pos++] = '\n';
|
||||||
|
}
|
||||||
|
p += 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 const struct file_operations regmap_map_fops = {
|
||||||
|
.open = regmap_open_file,
|
||||||
|
.read = regmap_map_read_file,
|
||||||
|
.llseek = default_llseek,
|
||||||
|
};
|
||||||
|
|
||||||
|
static ssize_t regmap_access_read_file(struct file *file,
|
||||||
|
char __user *user_buf, size_t count,
|
||||||
|
loff_t *ppos)
|
||||||
|
{
|
||||||
|
int reg_len, tot_len;
|
||||||
|
size_t buf_pos = 0;
|
||||||
|
loff_t p = 0;
|
||||||
|
ssize_t ret;
|
||||||
|
int i;
|
||||||
|
struct regmap *map = file->private_data;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
if (*ppos < 0 || !count)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
buf = kmalloc(count, GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Calculate the length of a fixed format */
|
||||||
|
reg_len = regmap_calc_reg_len(map->max_register, buf, count);
|
||||||
|
tot_len = reg_len + 10; /* ': R W V P\n' */
|
||||||
|
|
||||||
|
for (i = 0; i < map->max_register + 1; i++) {
|
||||||
|
/* Ignore registers which are neither readable nor writable */
|
||||||
|
if (!regmap_readable(map, i) && !regmap_writeable(map, i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* If we're in the region the user is trying to read */
|
||||||
|
if (p >= *ppos) {
|
||||||
|
/* ...but not beyond it */
|
||||||
|
if (buf_pos >= count - 1 - tot_len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Format the register */
|
||||||
|
snprintf(buf + buf_pos, count - buf_pos,
|
||||||
|
"%.*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');
|
||||||
|
|
||||||
|
buf_pos += tot_len;
|
||||||
|
}
|
||||||
|
p += 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 const struct file_operations regmap_access_fops = {
|
||||||
|
.open = regmap_open_file,
|
||||||
|
.read = regmap_access_read_file,
|
||||||
|
.llseek = default_llseek,
|
||||||
|
};
|
||||||
|
|
||||||
|
void regmap_debugfs_init(struct regmap *map)
|
||||||
|
{
|
||||||
|
map->debugfs = debugfs_create_dir(dev_name(map->dev),
|
||||||
|
regmap_debugfs_root);
|
||||||
|
if (!map->debugfs) {
|
||||||
|
dev_warn(map->dev, "Failed to create debugfs directory\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map->max_register) {
|
||||||
|
debugfs_create_file("registers", 0400, map->debugfs,
|
||||||
|
map, ®map_map_fops);
|
||||||
|
debugfs_create_file("access", 0400, map->debugfs,
|
||||||
|
map, ®map_access_fops);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void regmap_debugfs_exit(struct regmap *map)
|
||||||
|
{
|
||||||
|
debugfs_remove_recursive(map->debugfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void regmap_debugfs_initcall(void)
|
||||||
|
{
|
||||||
|
regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
|
||||||
|
if (!regmap_debugfs_root) {
|
||||||
|
pr_warn("regmap: Failed to create debugfs root\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,11 +90,9 @@ static int regmap_i2c_read(struct device *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct regmap_bus regmap_i2c = {
|
static struct regmap_bus regmap_i2c = {
|
||||||
.type = &i2c_bus_type,
|
|
||||||
.write = regmap_i2c_write,
|
.write = regmap_i2c_write,
|
||||||
.gather_write = regmap_i2c_gather_write,
|
.gather_write = regmap_i2c_gather_write,
|
||||||
.read = regmap_i2c_read,
|
.read = regmap_i2c_read,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -48,11 +48,9 @@ static int regmap_spi_read(struct device *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct regmap_bus regmap_spi = {
|
static struct regmap_bus regmap_spi = {
|
||||||
.type = &spi_bus_type,
|
|
||||||
.write = regmap_spi_write,
|
.write = regmap_spi_write,
|
||||||
.gather_write = regmap_spi_gather_write,
|
.gather_write = regmap_spi_gather_write,
|
||||||
.read = regmap_spi_read,
|
.read = regmap_spi_read,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.read_flag_mask = 0x80,
|
.read_flag_mask = 0x80,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,29 +15,54 @@
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
|
||||||
#include <linux/regmap.h>
|
#define CREATE_TRACE_POINTS
|
||||||
|
#include <trace/events/regmap.h>
|
||||||
|
|
||||||
struct regmap;
|
#include "internal.h"
|
||||||
|
|
||||||
struct regmap_format {
|
bool regmap_writeable(struct regmap *map, unsigned int reg)
|
||||||
size_t buf_size;
|
{
|
||||||
size_t reg_bytes;
|
if (map->max_register && reg > map->max_register)
|
||||||
size_t val_bytes;
|
return false;
|
||||||
void (*format_write)(struct regmap *map,
|
|
||||||
unsigned int reg, unsigned int val);
|
|
||||||
void (*format_reg)(void *buf, unsigned int reg);
|
|
||||||
void (*format_val)(void *buf, unsigned int val);
|
|
||||||
unsigned int (*parse_val)(void *buf);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct regmap {
|
if (map->writeable_reg)
|
||||||
struct mutex lock;
|
return map->writeable_reg(map->dev, reg);
|
||||||
|
|
||||||
struct device *dev; /* Device we do I/O on */
|
return true;
|
||||||
void *work_buf; /* Scratch buffer used to format I/O */
|
}
|
||||||
struct regmap_format format; /* Buffer format */
|
|
||||||
const struct regmap_bus *bus;
|
bool regmap_readable(struct regmap *map, unsigned int reg)
|
||||||
};
|
{
|
||||||
|
if (map->max_register && reg > map->max_register)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (map->readable_reg)
|
||||||
|
return map->readable_reg(map->dev, reg);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool regmap_volatile(struct regmap *map, unsigned int reg)
|
||||||
|
{
|
||||||
|
if (map->max_register && reg > map->max_register)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (map->volatile_reg)
|
||||||
|
return map->volatile_reg(map->dev, reg);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool regmap_precious(struct regmap *map, unsigned int reg)
|
||||||
|
{
|
||||||
|
if (map->max_register && reg > map->max_register)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (map->precious_reg)
|
||||||
|
return map->precious_reg(map->dev, reg);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void regmap_format_4_12_write(struct regmap *map,
|
static void regmap_format_4_12_write(struct regmap *map,
|
||||||
unsigned int reg, unsigned int val)
|
unsigned int reg, unsigned int val)
|
||||||
|
@ -116,6 +141,25 @@ struct regmap *regmap_init(struct device *dev,
|
||||||
map->format.val_bytes = config->val_bits / 8;
|
map->format.val_bytes = config->val_bits / 8;
|
||||||
map->dev = dev;
|
map->dev = dev;
|
||||||
map->bus = bus;
|
map->bus = bus;
|
||||||
|
map->max_register = config->max_register;
|
||||||
|
map->writeable_reg = config->writeable_reg;
|
||||||
|
map->readable_reg = config->readable_reg;
|
||||||
|
map->volatile_reg = config->volatile_reg;
|
||||||
|
map->precious_reg = config->precious_reg;
|
||||||
|
map->cache_type = config->cache_type;
|
||||||
|
map->reg_defaults = config->reg_defaults;
|
||||||
|
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_size_raw = (config->val_bits / 8) * config->num_reg_defaults_raw;
|
||||||
|
map->cache_word_size = config->val_bits / 8;
|
||||||
|
|
||||||
|
if (config->read_flag_mask || config->write_flag_mask) {
|
||||||
|
map->read_flag_mask = config->read_flag_mask;
|
||||||
|
map->write_flag_mask = config->write_flag_mask;
|
||||||
|
} else {
|
||||||
|
map->read_flag_mask = bus->read_flag_mask;
|
||||||
|
}
|
||||||
|
|
||||||
switch (config->reg_bits) {
|
switch (config->reg_bits) {
|
||||||
case 4:
|
case 4:
|
||||||
|
@ -171,6 +215,12 @@ struct regmap *regmap_init(struct device *dev,
|
||||||
goto err_map;
|
goto err_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = regcache_init(map);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_map;
|
||||||
|
|
||||||
|
regmap_debugfs_init(map);
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
|
|
||||||
err_map:
|
err_map:
|
||||||
|
@ -185,6 +235,8 @@ EXPORT_SYMBOL_GPL(regmap_init);
|
||||||
*/
|
*/
|
||||||
void regmap_exit(struct regmap *map)
|
void regmap_exit(struct regmap *map)
|
||||||
{
|
{
|
||||||
|
regcache_exit(map);
|
||||||
|
regmap_debugfs_exit(map);
|
||||||
kfree(map->work_buf);
|
kfree(map->work_buf);
|
||||||
kfree(map);
|
kfree(map);
|
||||||
}
|
}
|
||||||
|
@ -193,19 +245,38 @@ EXPORT_SYMBOL_GPL(regmap_exit);
|
||||||
static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||||
const void *val, size_t val_len)
|
const void *val, size_t val_len)
|
||||||
{
|
{
|
||||||
|
u8 *u8 = map->work_buf;
|
||||||
void *buf;
|
void *buf;
|
||||||
int ret = -ENOTSUPP;
|
int ret = -ENOTSUPP;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Check for unwritable registers before we start */
|
||||||
|
if (map->writeable_reg)
|
||||||
|
for (i = 0; i < val_len / map->format.val_bytes; i++)
|
||||||
|
if (!map->writeable_reg(map->dev, reg + i))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
map->format.format_reg(map->work_buf, reg);
|
map->format.format_reg(map->work_buf, reg);
|
||||||
|
|
||||||
/* Try to do a gather write if we can */
|
u8[0] |= map->write_flag_mask;
|
||||||
if (map->bus->gather_write)
|
|
||||||
|
trace_regmap_hw_write_start(map->dev, reg,
|
||||||
|
val_len / map->format.val_bytes);
|
||||||
|
|
||||||
|
/* If we're doing a single register write we can probably just
|
||||||
|
* send the work_buf directly, otherwise try to do a gather
|
||||||
|
* write.
|
||||||
|
*/
|
||||||
|
if (val == map->work_buf + map->format.reg_bytes)
|
||||||
|
ret = map->bus->write(map->dev, map->work_buf,
|
||||||
|
map->format.reg_bytes + val_len);
|
||||||
|
else if (map->bus->gather_write)
|
||||||
ret = map->bus->gather_write(map->dev, map->work_buf,
|
ret = map->bus->gather_write(map->dev, map->work_buf,
|
||||||
map->format.reg_bytes,
|
map->format.reg_bytes,
|
||||||
val, val_len);
|
val, val_len);
|
||||||
|
|
||||||
/* Otherwise fall back on linearising by hand. */
|
/* If that didn't work fall back on linearising by hand. */
|
||||||
if (ret == -ENOTSUPP) {
|
if (ret == -ENOTSUPP) {
|
||||||
len = map->format.reg_bytes + val_len;
|
len = map->format.reg_bytes + val_len;
|
||||||
buf = kmalloc(len, GFP_KERNEL);
|
buf = kmalloc(len, GFP_KERNEL);
|
||||||
|
@ -219,19 +290,39 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace_regmap_hw_write_done(map->dev, reg,
|
||||||
|
val_len / map->format.val_bytes);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _regmap_write(struct regmap *map, unsigned int reg,
|
int _regmap_write(struct regmap *map, unsigned int reg,
|
||||||
unsigned int val)
|
unsigned int val)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
BUG_ON(!map->format.format_write && !map->format.format_val);
|
BUG_ON(!map->format.format_write && !map->format.format_val);
|
||||||
|
|
||||||
|
if (!map->cache_bypass) {
|
||||||
|
ret = regcache_write(map, reg, val);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
if (map->cache_only)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_regmap_reg_write(map->dev, reg, val);
|
||||||
|
|
||||||
if (map->format.format_write) {
|
if (map->format.format_write) {
|
||||||
map->format.format_write(map, reg, val);
|
map->format.format_write(map, reg, val);
|
||||||
|
|
||||||
return map->bus->write(map->dev, map->work_buf,
|
trace_regmap_hw_write_start(map->dev, reg, 1);
|
||||||
|
|
||||||
|
ret = map->bus->write(map->dev, map->work_buf,
|
||||||
map->format.buf_size);
|
map->format.buf_size);
|
||||||
|
|
||||||
|
trace_regmap_hw_write_done(map->dev, reg, 1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
map->format.format_val(map->work_buf + map->format.reg_bytes,
|
map->format.format_val(map->work_buf + map->format.reg_bytes,
|
||||||
val);
|
val);
|
||||||
|
@ -286,6 +377,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
WARN_ON(map->cache_type != REGCACHE_NONE);
|
||||||
|
|
||||||
mutex_lock(&map->lock);
|
mutex_lock(&map->lock);
|
||||||
|
|
||||||
ret = _regmap_raw_write(map, reg, val, val_len);
|
ret = _regmap_raw_write(map, reg, val, val_len);
|
||||||
|
@ -305,20 +398,23 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
||||||
map->format.format_reg(map->work_buf, reg);
|
map->format.format_reg(map->work_buf, reg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some buses flag reads by setting the high bits in the
|
* Some buses or devices flag reads by setting the high bits in the
|
||||||
* register addresss; since it's always the high bits for all
|
* register addresss; since it's always the high bits for all
|
||||||
* current formats we can do this here rather than in
|
* current formats we can do this here rather than in
|
||||||
* formatting. This may break if we get interesting formats.
|
* formatting. This may break if we get interesting formats.
|
||||||
*/
|
*/
|
||||||
if (map->bus->read_flag_mask)
|
u8[0] |= map->read_flag_mask;
|
||||||
u8[0] |= map->bus->read_flag_mask;
|
|
||||||
|
trace_regmap_hw_read_start(map->dev, reg,
|
||||||
|
val_len / map->format.val_bytes);
|
||||||
|
|
||||||
ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes,
|
ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes,
|
||||||
val, val_len);
|
val, val_len);
|
||||||
if (ret != 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
trace_regmap_hw_read_done(map->dev, reg,
|
||||||
|
val_len / map->format.val_bytes);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _regmap_read(struct regmap *map, unsigned int reg,
|
static int _regmap_read(struct regmap *map, unsigned int reg,
|
||||||
|
@ -329,9 +425,20 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
|
||||||
if (!map->format.parse_val)
|
if (!map->format.parse_val)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
|
if (!map->cache_bypass) {
|
||||||
|
ret = regcache_read(map, reg, val);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map->cache_only)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
|
||||||
|
if (ret == 0) {
|
||||||
*val = map->format.parse_val(map->work_buf);
|
*val = map->format.parse_val(map->work_buf);
|
||||||
|
trace_regmap_reg_read(map->dev, reg, *val);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -375,6 +482,14 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
||||||
size_t val_len)
|
size_t val_len)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
int i;
|
||||||
|
bool vol = true;
|
||||||
|
|
||||||
|
for (i = 0; i < val_len / map->format.val_bytes; i++)
|
||||||
|
if (!regmap_volatile(map, reg + i))
|
||||||
|
vol = false;
|
||||||
|
|
||||||
|
WARN_ON(!vol && map->cache_type != REGCACHE_NONE);
|
||||||
|
|
||||||
mutex_lock(&map->lock);
|
mutex_lock(&map->lock);
|
||||||
|
|
||||||
|
@ -402,16 +517,30 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
|
||||||
{
|
{
|
||||||
int ret, i;
|
int ret, i;
|
||||||
size_t val_bytes = map->format.val_bytes;
|
size_t val_bytes = map->format.val_bytes;
|
||||||
|
bool vol = true;
|
||||||
|
|
||||||
if (!map->format.parse_val)
|
if (!map->format.parse_val)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Is this a block of volatile registers? */
|
||||||
|
for (i = 0; i < val_count; i++)
|
||||||
|
if (!regmap_volatile(map, reg + i))
|
||||||
|
vol = false;
|
||||||
|
|
||||||
|
if (vol || map->cache_type == REGCACHE_NONE) {
|
||||||
ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
|
ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
for (i = 0; i < val_count * val_bytes; i += val_bytes)
|
for (i = 0; i < val_count * val_bytes; i += val_bytes)
|
||||||
map->format.parse_val(val + i);
|
map->format.parse_val(val + i);
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < val_count; i++) {
|
||||||
|
ret = regmap_read(map, reg + i, val + (i * val_bytes));
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -450,3 +579,11 @@ int regmap_update_bits(struct regmap *map, unsigned int reg,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(regmap_update_bits);
|
EXPORT_SYMBOL_GPL(regmap_update_bits);
|
||||||
|
|
||||||
|
static int __init regmap_initcall(void)
|
||||||
|
{
|
||||||
|
regmap_debugfs_initcall();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
postcore_initcall(regmap_initcall);
|
||||||
|
|
|
@ -404,6 +404,7 @@ config MFD_WM831X_I2C
|
||||||
bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C"
|
bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C"
|
||||||
select MFD_CORE
|
select MFD_CORE
|
||||||
select MFD_WM831X
|
select MFD_WM831X
|
||||||
|
select REGMAP_I2C
|
||||||
depends on I2C=y && GENERIC_HARDIRQS
|
depends on I2C=y && GENERIC_HARDIRQS
|
||||||
help
|
help
|
||||||
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
|
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
|
||||||
|
@ -415,6 +416,7 @@ config MFD_WM831X_SPI
|
||||||
bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI"
|
bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI"
|
||||||
select MFD_CORE
|
select MFD_CORE
|
||||||
select MFD_WM831X
|
select MFD_WM831X
|
||||||
|
select REGMAP_SPI
|
||||||
depends on SPI_MASTER && GENERIC_HARDIRQS
|
depends on SPI_MASTER && GENERIC_HARDIRQS
|
||||||
help
|
help
|
||||||
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
|
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
|
||||||
|
@ -488,6 +490,7 @@ config MFD_WM8350_I2C
|
||||||
config MFD_WM8994
|
config MFD_WM8994
|
||||||
bool "Support Wolfson Microelectronics WM8994"
|
bool "Support Wolfson Microelectronics WM8994"
|
||||||
select MFD_CORE
|
select MFD_CORE
|
||||||
|
select REGMAP_I2C
|
||||||
depends on I2C=y && GENERIC_HARDIRQS
|
depends on I2C=y && GENERIC_HARDIRQS
|
||||||
help
|
help
|
||||||
The WM8994 is a highly integrated hi-fi CODEC designed for
|
The WM8994 is a highly integrated hi-fi CODEC designed for
|
||||||
|
|
|
@ -18,12 +18,14 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
|
||||||
#include <linux/mfd/wm831x/core.h>
|
#include <linux/mfd/wm831x/core.h>
|
||||||
#include <linux/mfd/wm831x/pdata.h>
|
#include <linux/mfd/wm831x/pdata.h>
|
||||||
#include <linux/mfd/wm831x/irq.h>
|
#include <linux/mfd/wm831x/irq.h>
|
||||||
#include <linux/mfd/wm831x/auxadc.h>
|
#include <linux/mfd/wm831x/auxadc.h>
|
||||||
#include <linux/mfd/wm831x/otp.h>
|
#include <linux/mfd/wm831x/otp.h>
|
||||||
|
#include <linux/mfd/wm831x/pmu.h>
|
||||||
#include <linux/mfd/wm831x/regulator.h>
|
#include <linux/mfd/wm831x/regulator.h>
|
||||||
|
|
||||||
/* Current settings - values are 2*2^(reg_val/4) microamps. These are
|
/* Current settings - values are 2*2^(reg_val/4) microamps. These are
|
||||||
|
@ -160,27 +162,350 @@ int wm831x_reg_unlock(struct wm831x *wm831x)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(wm831x_reg_unlock);
|
EXPORT_SYMBOL_GPL(wm831x_reg_unlock);
|
||||||
|
|
||||||
static int wm831x_read(struct wm831x *wm831x, unsigned short reg,
|
static bool wm831x_reg_readable(struct device *dev, unsigned int reg)
|
||||||
int bytes, void *dest)
|
|
||||||
{
|
{
|
||||||
int ret, i;
|
switch (reg) {
|
||||||
u16 *buf = dest;
|
case WM831X_RESET_ID:
|
||||||
|
case WM831X_REVISION:
|
||||||
BUG_ON(bytes % 2);
|
case WM831X_PARENT_ID:
|
||||||
BUG_ON(bytes <= 0);
|
case WM831X_SYSVDD_CONTROL:
|
||||||
|
case WM831X_THERMAL_MONITORING:
|
||||||
ret = wm831x->read_dev(wm831x, reg, bytes, dest);
|
case WM831X_POWER_STATE:
|
||||||
if (ret < 0)
|
case WM831X_WATCHDOG:
|
||||||
return ret;
|
case WM831X_ON_PIN_CONTROL:
|
||||||
|
case WM831X_RESET_CONTROL:
|
||||||
for (i = 0; i < bytes / 2; i++) {
|
case WM831X_CONTROL_INTERFACE:
|
||||||
buf[i] = be16_to_cpu(buf[i]);
|
case WM831X_SECURITY_KEY:
|
||||||
|
case WM831X_SOFTWARE_SCRATCH:
|
||||||
dev_vdbg(wm831x->dev, "Read %04x from R%d(0x%x)\n",
|
case WM831X_OTP_CONTROL:
|
||||||
buf[i], reg + i, reg + i);
|
case WM831X_GPIO_LEVEL:
|
||||||
|
case WM831X_SYSTEM_STATUS:
|
||||||
|
case WM831X_ON_SOURCE:
|
||||||
|
case WM831X_OFF_SOURCE:
|
||||||
|
case WM831X_SYSTEM_INTERRUPTS:
|
||||||
|
case WM831X_INTERRUPT_STATUS_1:
|
||||||
|
case WM831X_INTERRUPT_STATUS_2:
|
||||||
|
case WM831X_INTERRUPT_STATUS_3:
|
||||||
|
case WM831X_INTERRUPT_STATUS_4:
|
||||||
|
case WM831X_INTERRUPT_STATUS_5:
|
||||||
|
case WM831X_IRQ_CONFIG:
|
||||||
|
case WM831X_SYSTEM_INTERRUPTS_MASK:
|
||||||
|
case WM831X_INTERRUPT_STATUS_1_MASK:
|
||||||
|
case WM831X_INTERRUPT_STATUS_2_MASK:
|
||||||
|
case WM831X_INTERRUPT_STATUS_3_MASK:
|
||||||
|
case WM831X_INTERRUPT_STATUS_4_MASK:
|
||||||
|
case WM831X_INTERRUPT_STATUS_5_MASK:
|
||||||
|
case WM831X_RTC_WRITE_COUNTER:
|
||||||
|
case WM831X_RTC_TIME_1:
|
||||||
|
case WM831X_RTC_TIME_2:
|
||||||
|
case WM831X_RTC_ALARM_1:
|
||||||
|
case WM831X_RTC_ALARM_2:
|
||||||
|
case WM831X_RTC_CONTROL:
|
||||||
|
case WM831X_RTC_TRIM:
|
||||||
|
case WM831X_TOUCH_CONTROL_1:
|
||||||
|
case WM831X_TOUCH_CONTROL_2:
|
||||||
|
case WM831X_TOUCH_DATA_X:
|
||||||
|
case WM831X_TOUCH_DATA_Y:
|
||||||
|
case WM831X_TOUCH_DATA_Z:
|
||||||
|
case WM831X_AUXADC_DATA:
|
||||||
|
case WM831X_AUXADC_CONTROL:
|
||||||
|
case WM831X_AUXADC_SOURCE:
|
||||||
|
case WM831X_COMPARATOR_CONTROL:
|
||||||
|
case WM831X_COMPARATOR_1:
|
||||||
|
case WM831X_COMPARATOR_2:
|
||||||
|
case WM831X_COMPARATOR_3:
|
||||||
|
case WM831X_COMPARATOR_4:
|
||||||
|
case WM831X_GPIO1_CONTROL:
|
||||||
|
case WM831X_GPIO2_CONTROL:
|
||||||
|
case WM831X_GPIO3_CONTROL:
|
||||||
|
case WM831X_GPIO4_CONTROL:
|
||||||
|
case WM831X_GPIO5_CONTROL:
|
||||||
|
case WM831X_GPIO6_CONTROL:
|
||||||
|
case WM831X_GPIO7_CONTROL:
|
||||||
|
case WM831X_GPIO8_CONTROL:
|
||||||
|
case WM831X_GPIO9_CONTROL:
|
||||||
|
case WM831X_GPIO10_CONTROL:
|
||||||
|
case WM831X_GPIO11_CONTROL:
|
||||||
|
case WM831X_GPIO12_CONTROL:
|
||||||
|
case WM831X_GPIO13_CONTROL:
|
||||||
|
case WM831X_GPIO14_CONTROL:
|
||||||
|
case WM831X_GPIO15_CONTROL:
|
||||||
|
case WM831X_GPIO16_CONTROL:
|
||||||
|
case WM831X_CHARGER_CONTROL_1:
|
||||||
|
case WM831X_CHARGER_CONTROL_2:
|
||||||
|
case WM831X_CHARGER_STATUS:
|
||||||
|
case WM831X_BACKUP_CHARGER_CONTROL:
|
||||||
|
case WM831X_STATUS_LED_1:
|
||||||
|
case WM831X_STATUS_LED_2:
|
||||||
|
case WM831X_CURRENT_SINK_1:
|
||||||
|
case WM831X_CURRENT_SINK_2:
|
||||||
|
case WM831X_DCDC_ENABLE:
|
||||||
|
case WM831X_LDO_ENABLE:
|
||||||
|
case WM831X_DCDC_STATUS:
|
||||||
|
case WM831X_LDO_STATUS:
|
||||||
|
case WM831X_DCDC_UV_STATUS:
|
||||||
|
case WM831X_LDO_UV_STATUS:
|
||||||
|
case WM831X_DC1_CONTROL_1:
|
||||||
|
case WM831X_DC1_CONTROL_2:
|
||||||
|
case WM831X_DC1_ON_CONFIG:
|
||||||
|
case WM831X_DC1_SLEEP_CONTROL:
|
||||||
|
case WM831X_DC1_DVS_CONTROL:
|
||||||
|
case WM831X_DC2_CONTROL_1:
|
||||||
|
case WM831X_DC2_CONTROL_2:
|
||||||
|
case WM831X_DC2_ON_CONFIG:
|
||||||
|
case WM831X_DC2_SLEEP_CONTROL:
|
||||||
|
case WM831X_DC2_DVS_CONTROL:
|
||||||
|
case WM831X_DC3_CONTROL_1:
|
||||||
|
case WM831X_DC3_CONTROL_2:
|
||||||
|
case WM831X_DC3_ON_CONFIG:
|
||||||
|
case WM831X_DC3_SLEEP_CONTROL:
|
||||||
|
case WM831X_DC4_CONTROL:
|
||||||
|
case WM831X_DC4_SLEEP_CONTROL:
|
||||||
|
case WM831X_EPE1_CONTROL:
|
||||||
|
case WM831X_EPE2_CONTROL:
|
||||||
|
case WM831X_LDO1_CONTROL:
|
||||||
|
case WM831X_LDO1_ON_CONTROL:
|
||||||
|
case WM831X_LDO1_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO2_CONTROL:
|
||||||
|
case WM831X_LDO2_ON_CONTROL:
|
||||||
|
case WM831X_LDO2_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO3_CONTROL:
|
||||||
|
case WM831X_LDO3_ON_CONTROL:
|
||||||
|
case WM831X_LDO3_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO4_CONTROL:
|
||||||
|
case WM831X_LDO4_ON_CONTROL:
|
||||||
|
case WM831X_LDO4_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO5_CONTROL:
|
||||||
|
case WM831X_LDO5_ON_CONTROL:
|
||||||
|
case WM831X_LDO5_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO6_CONTROL:
|
||||||
|
case WM831X_LDO6_ON_CONTROL:
|
||||||
|
case WM831X_LDO6_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO7_CONTROL:
|
||||||
|
case WM831X_LDO7_ON_CONTROL:
|
||||||
|
case WM831X_LDO7_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO8_CONTROL:
|
||||||
|
case WM831X_LDO8_ON_CONTROL:
|
||||||
|
case WM831X_LDO8_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO9_CONTROL:
|
||||||
|
case WM831X_LDO9_ON_CONTROL:
|
||||||
|
case WM831X_LDO9_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO10_CONTROL:
|
||||||
|
case WM831X_LDO10_ON_CONTROL:
|
||||||
|
case WM831X_LDO10_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO11_ON_CONTROL:
|
||||||
|
case WM831X_LDO11_SLEEP_CONTROL:
|
||||||
|
case WM831X_POWER_GOOD_SOURCE_1:
|
||||||
|
case WM831X_POWER_GOOD_SOURCE_2:
|
||||||
|
case WM831X_CLOCK_CONTROL_1:
|
||||||
|
case WM831X_CLOCK_CONTROL_2:
|
||||||
|
case WM831X_FLL_CONTROL_1:
|
||||||
|
case WM831X_FLL_CONTROL_2:
|
||||||
|
case WM831X_FLL_CONTROL_3:
|
||||||
|
case WM831X_FLL_CONTROL_4:
|
||||||
|
case WM831X_FLL_CONTROL_5:
|
||||||
|
case WM831X_UNIQUE_ID_1:
|
||||||
|
case WM831X_UNIQUE_ID_2:
|
||||||
|
case WM831X_UNIQUE_ID_3:
|
||||||
|
case WM831X_UNIQUE_ID_4:
|
||||||
|
case WM831X_UNIQUE_ID_5:
|
||||||
|
case WM831X_UNIQUE_ID_6:
|
||||||
|
case WM831X_UNIQUE_ID_7:
|
||||||
|
case WM831X_UNIQUE_ID_8:
|
||||||
|
case WM831X_FACTORY_OTP_ID:
|
||||||
|
case WM831X_FACTORY_OTP_1:
|
||||||
|
case WM831X_FACTORY_OTP_2:
|
||||||
|
case WM831X_FACTORY_OTP_3:
|
||||||
|
case WM831X_FACTORY_OTP_4:
|
||||||
|
case WM831X_FACTORY_OTP_5:
|
||||||
|
case WM831X_CUSTOMER_OTP_ID:
|
||||||
|
case WM831X_DC1_OTP_CONTROL:
|
||||||
|
case WM831X_DC2_OTP_CONTROL:
|
||||||
|
case WM831X_DC3_OTP_CONTROL:
|
||||||
|
case WM831X_LDO1_2_OTP_CONTROL:
|
||||||
|
case WM831X_LDO3_4_OTP_CONTROL:
|
||||||
|
case WM831X_LDO5_6_OTP_CONTROL:
|
||||||
|
case WM831X_LDO7_8_OTP_CONTROL:
|
||||||
|
case WM831X_LDO9_10_OTP_CONTROL:
|
||||||
|
case WM831X_LDO11_EPE_CONTROL:
|
||||||
|
case WM831X_GPIO1_OTP_CONTROL:
|
||||||
|
case WM831X_GPIO2_OTP_CONTROL:
|
||||||
|
case WM831X_GPIO3_OTP_CONTROL:
|
||||||
|
case WM831X_GPIO4_OTP_CONTROL:
|
||||||
|
case WM831X_GPIO5_OTP_CONTROL:
|
||||||
|
case WM831X_GPIO6_OTP_CONTROL:
|
||||||
|
case WM831X_DBE_CHECK_DATA:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
static bool wm831x_reg_writeable(struct device *dev, unsigned int reg)
|
||||||
|
{
|
||||||
|
struct wm831x *wm831x = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (wm831x_reg_locked(wm831x, reg))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case WM831X_SYSVDD_CONTROL:
|
||||||
|
case WM831X_THERMAL_MONITORING:
|
||||||
|
case WM831X_POWER_STATE:
|
||||||
|
case WM831X_WATCHDOG:
|
||||||
|
case WM831X_ON_PIN_CONTROL:
|
||||||
|
case WM831X_RESET_CONTROL:
|
||||||
|
case WM831X_CONTROL_INTERFACE:
|
||||||
|
case WM831X_SECURITY_KEY:
|
||||||
|
case WM831X_SOFTWARE_SCRATCH:
|
||||||
|
case WM831X_OTP_CONTROL:
|
||||||
|
case WM831X_GPIO_LEVEL:
|
||||||
|
case WM831X_INTERRUPT_STATUS_1:
|
||||||
|
case WM831X_INTERRUPT_STATUS_2:
|
||||||
|
case WM831X_INTERRUPT_STATUS_3:
|
||||||
|
case WM831X_INTERRUPT_STATUS_4:
|
||||||
|
case WM831X_INTERRUPT_STATUS_5:
|
||||||
|
case WM831X_IRQ_CONFIG:
|
||||||
|
case WM831X_SYSTEM_INTERRUPTS_MASK:
|
||||||
|
case WM831X_INTERRUPT_STATUS_1_MASK:
|
||||||
|
case WM831X_INTERRUPT_STATUS_2_MASK:
|
||||||
|
case WM831X_INTERRUPT_STATUS_3_MASK:
|
||||||
|
case WM831X_INTERRUPT_STATUS_4_MASK:
|
||||||
|
case WM831X_INTERRUPT_STATUS_5_MASK:
|
||||||
|
case WM831X_RTC_TIME_1:
|
||||||
|
case WM831X_RTC_TIME_2:
|
||||||
|
case WM831X_RTC_ALARM_1:
|
||||||
|
case WM831X_RTC_ALARM_2:
|
||||||
|
case WM831X_RTC_CONTROL:
|
||||||
|
case WM831X_RTC_TRIM:
|
||||||
|
case WM831X_TOUCH_CONTROL_1:
|
||||||
|
case WM831X_TOUCH_CONTROL_2:
|
||||||
|
case WM831X_AUXADC_CONTROL:
|
||||||
|
case WM831X_AUXADC_SOURCE:
|
||||||
|
case WM831X_COMPARATOR_CONTROL:
|
||||||
|
case WM831X_COMPARATOR_1:
|
||||||
|
case WM831X_COMPARATOR_2:
|
||||||
|
case WM831X_COMPARATOR_3:
|
||||||
|
case WM831X_COMPARATOR_4:
|
||||||
|
case WM831X_GPIO1_CONTROL:
|
||||||
|
case WM831X_GPIO2_CONTROL:
|
||||||
|
case WM831X_GPIO3_CONTROL:
|
||||||
|
case WM831X_GPIO4_CONTROL:
|
||||||
|
case WM831X_GPIO5_CONTROL:
|
||||||
|
case WM831X_GPIO6_CONTROL:
|
||||||
|
case WM831X_GPIO7_CONTROL:
|
||||||
|
case WM831X_GPIO8_CONTROL:
|
||||||
|
case WM831X_GPIO9_CONTROL:
|
||||||
|
case WM831X_GPIO10_CONTROL:
|
||||||
|
case WM831X_GPIO11_CONTROL:
|
||||||
|
case WM831X_GPIO12_CONTROL:
|
||||||
|
case WM831X_GPIO13_CONTROL:
|
||||||
|
case WM831X_GPIO14_CONTROL:
|
||||||
|
case WM831X_GPIO15_CONTROL:
|
||||||
|
case WM831X_GPIO16_CONTROL:
|
||||||
|
case WM831X_CHARGER_CONTROL_1:
|
||||||
|
case WM831X_CHARGER_CONTROL_2:
|
||||||
|
case WM831X_CHARGER_STATUS:
|
||||||
|
case WM831X_BACKUP_CHARGER_CONTROL:
|
||||||
|
case WM831X_STATUS_LED_1:
|
||||||
|
case WM831X_STATUS_LED_2:
|
||||||
|
case WM831X_CURRENT_SINK_1:
|
||||||
|
case WM831X_CURRENT_SINK_2:
|
||||||
|
case WM831X_DCDC_ENABLE:
|
||||||
|
case WM831X_LDO_ENABLE:
|
||||||
|
case WM831X_DC1_CONTROL_1:
|
||||||
|
case WM831X_DC1_CONTROL_2:
|
||||||
|
case WM831X_DC1_ON_CONFIG:
|
||||||
|
case WM831X_DC1_SLEEP_CONTROL:
|
||||||
|
case WM831X_DC1_DVS_CONTROL:
|
||||||
|
case WM831X_DC2_CONTROL_1:
|
||||||
|
case WM831X_DC2_CONTROL_2:
|
||||||
|
case WM831X_DC2_ON_CONFIG:
|
||||||
|
case WM831X_DC2_SLEEP_CONTROL:
|
||||||
|
case WM831X_DC2_DVS_CONTROL:
|
||||||
|
case WM831X_DC3_CONTROL_1:
|
||||||
|
case WM831X_DC3_CONTROL_2:
|
||||||
|
case WM831X_DC3_ON_CONFIG:
|
||||||
|
case WM831X_DC3_SLEEP_CONTROL:
|
||||||
|
case WM831X_DC4_CONTROL:
|
||||||
|
case WM831X_DC4_SLEEP_CONTROL:
|
||||||
|
case WM831X_EPE1_CONTROL:
|
||||||
|
case WM831X_EPE2_CONTROL:
|
||||||
|
case WM831X_LDO1_CONTROL:
|
||||||
|
case WM831X_LDO1_ON_CONTROL:
|
||||||
|
case WM831X_LDO1_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO2_CONTROL:
|
||||||
|
case WM831X_LDO2_ON_CONTROL:
|
||||||
|
case WM831X_LDO2_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO3_CONTROL:
|
||||||
|
case WM831X_LDO3_ON_CONTROL:
|
||||||
|
case WM831X_LDO3_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO4_CONTROL:
|
||||||
|
case WM831X_LDO4_ON_CONTROL:
|
||||||
|
case WM831X_LDO4_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO5_CONTROL:
|
||||||
|
case WM831X_LDO5_ON_CONTROL:
|
||||||
|
case WM831X_LDO5_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO6_CONTROL:
|
||||||
|
case WM831X_LDO6_ON_CONTROL:
|
||||||
|
case WM831X_LDO6_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO7_CONTROL:
|
||||||
|
case WM831X_LDO7_ON_CONTROL:
|
||||||
|
case WM831X_LDO7_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO8_CONTROL:
|
||||||
|
case WM831X_LDO8_ON_CONTROL:
|
||||||
|
case WM831X_LDO8_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO9_CONTROL:
|
||||||
|
case WM831X_LDO9_ON_CONTROL:
|
||||||
|
case WM831X_LDO9_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO10_CONTROL:
|
||||||
|
case WM831X_LDO10_ON_CONTROL:
|
||||||
|
case WM831X_LDO10_SLEEP_CONTROL:
|
||||||
|
case WM831X_LDO11_ON_CONTROL:
|
||||||
|
case WM831X_LDO11_SLEEP_CONTROL:
|
||||||
|
case WM831X_POWER_GOOD_SOURCE_1:
|
||||||
|
case WM831X_POWER_GOOD_SOURCE_2:
|
||||||
|
case WM831X_CLOCK_CONTROL_1:
|
||||||
|
case WM831X_CLOCK_CONTROL_2:
|
||||||
|
case WM831X_FLL_CONTROL_1:
|
||||||
|
case WM831X_FLL_CONTROL_2:
|
||||||
|
case WM831X_FLL_CONTROL_3:
|
||||||
|
case WM831X_FLL_CONTROL_4:
|
||||||
|
case WM831X_FLL_CONTROL_5:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool wm831x_reg_volatile(struct device *dev, unsigned int reg)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case WM831X_SYSTEM_STATUS:
|
||||||
|
case WM831X_ON_SOURCE:
|
||||||
|
case WM831X_OFF_SOURCE:
|
||||||
|
case WM831X_GPIO_LEVEL:
|
||||||
|
case WM831X_SYSTEM_INTERRUPTS:
|
||||||
|
case WM831X_INTERRUPT_STATUS_1:
|
||||||
|
case WM831X_INTERRUPT_STATUS_2:
|
||||||
|
case WM831X_INTERRUPT_STATUS_3:
|
||||||
|
case WM831X_INTERRUPT_STATUS_4:
|
||||||
|
case WM831X_INTERRUPT_STATUS_5:
|
||||||
|
case WM831X_RTC_TIME_1:
|
||||||
|
case WM831X_RTC_TIME_2:
|
||||||
|
case WM831X_TOUCH_DATA_X:
|
||||||
|
case WM831X_TOUCH_DATA_Y:
|
||||||
|
case WM831X_TOUCH_DATA_Z:
|
||||||
|
case WM831X_AUXADC_DATA:
|
||||||
|
case WM831X_CHARGER_STATUS:
|
||||||
|
case WM831X_DCDC_STATUS:
|
||||||
|
case WM831X_LDO_STATUS:
|
||||||
|
case WM831X_DCDC_UV_STATUS:
|
||||||
|
case WM831X_LDO_UV_STATUS:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -191,14 +516,10 @@ static int wm831x_read(struct wm831x *wm831x, unsigned short reg,
|
||||||
*/
|
*/
|
||||||
int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg)
|
int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg)
|
||||||
{
|
{
|
||||||
unsigned short val;
|
unsigned int val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&wm831x->io_lock);
|
ret = regmap_read(wm831x->regmap, reg, &val);
|
||||||
|
|
||||||
ret = wm831x_read(wm831x, reg, 2, &val);
|
|
||||||
|
|
||||||
mutex_unlock(&wm831x->io_lock);
|
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -218,15 +539,7 @@ EXPORT_SYMBOL_GPL(wm831x_reg_read);
|
||||||
int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
|
int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
|
||||||
int count, u16 *buf)
|
int count, u16 *buf)
|
||||||
{
|
{
|
||||||
int ret;
|
return regmap_bulk_read(wm831x->regmap, reg, buf, count);
|
||||||
|
|
||||||
mutex_lock(&wm831x->io_lock);
|
|
||||||
|
|
||||||
ret = wm831x_read(wm831x, reg, count * 2, buf);
|
|
||||||
|
|
||||||
mutex_unlock(&wm831x->io_lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(wm831x_bulk_read);
|
EXPORT_SYMBOL_GPL(wm831x_bulk_read);
|
||||||
|
|
||||||
|
@ -234,7 +547,7 @@ static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
|
||||||
int bytes, void *src)
|
int bytes, void *src)
|
||||||
{
|
{
|
||||||
u16 *buf = src;
|
u16 *buf = src;
|
||||||
int i;
|
int i, ret;
|
||||||
|
|
||||||
BUG_ON(bytes % 2);
|
BUG_ON(bytes % 2);
|
||||||
BUG_ON(bytes <= 0);
|
BUG_ON(bytes <= 0);
|
||||||
|
@ -245,11 +558,10 @@ static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
|
||||||
|
|
||||||
dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n",
|
dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n",
|
||||||
buf[i], reg + i, reg + i);
|
buf[i], reg + i, reg + i);
|
||||||
|
ret = regmap_write(wm831x->regmap, reg + i, buf[i]);
|
||||||
buf[i] = cpu_to_be16(buf[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wm831x->write_dev(wm831x, reg, bytes, src);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -286,20 +598,14 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
|
||||||
unsigned short mask, unsigned short val)
|
unsigned short mask, unsigned short val)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
u16 r;
|
|
||||||
|
|
||||||
mutex_lock(&wm831x->io_lock);
|
mutex_lock(&wm831x->io_lock);
|
||||||
|
|
||||||
ret = wm831x_read(wm831x, reg, 2, &r);
|
if (!wm831x_reg_locked(wm831x, reg))
|
||||||
if (ret < 0)
|
ret = regmap_update_bits(wm831x->regmap, reg, mask, val);
|
||||||
goto out;
|
else
|
||||||
|
ret = -EPERM;
|
||||||
|
|
||||||
r &= ~mask;
|
|
||||||
r |= val & mask;
|
|
||||||
|
|
||||||
ret = wm831x_write(wm831x, reg, 2, &r);
|
|
||||||
|
|
||||||
out:
|
|
||||||
mutex_unlock(&wm831x->io_lock);
|
mutex_unlock(&wm831x->io_lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1292,6 +1598,19 @@ static struct mfd_cell backlight_devs[] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct regmap_config wm831x_regmap_config = {
|
||||||
|
.reg_bits = 16,
|
||||||
|
.val_bits = 16,
|
||||||
|
|
||||||
|
.cache_type = REGCACHE_RBTREE,
|
||||||
|
|
||||||
|
.max_register = WM831X_DBE_CHECK_DATA,
|
||||||
|
.readable_reg = wm831x_reg_readable,
|
||||||
|
.writeable_reg = wm831x_reg_writeable,
|
||||||
|
.volatile_reg = wm831x_reg_volatile,
|
||||||
|
};
|
||||||
|
EXPORT_SYMBOL_GPL(wm831x_regmap_config);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Instantiate the generic non-control parts of the device.
|
* Instantiate the generic non-control parts of the device.
|
||||||
*/
|
*/
|
||||||
|
@ -1305,11 +1624,12 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||||
mutex_init(&wm831x->io_lock);
|
mutex_init(&wm831x->io_lock);
|
||||||
mutex_init(&wm831x->key_lock);
|
mutex_init(&wm831x->key_lock);
|
||||||
dev_set_drvdata(wm831x->dev, wm831x);
|
dev_set_drvdata(wm831x->dev, wm831x);
|
||||||
|
wm831x->soft_shutdown = pdata->soft_shutdown;
|
||||||
|
|
||||||
ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
|
ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret);
|
dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret);
|
||||||
goto err;
|
goto err_regmap;
|
||||||
}
|
}
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case 0x6204:
|
case 0x6204:
|
||||||
|
@ -1318,20 +1638,20 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||||
default:
|
default:
|
||||||
dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret);
|
dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto err;
|
goto err_regmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = wm831x_reg_read(wm831x, WM831X_REVISION);
|
ret = wm831x_reg_read(wm831x, WM831X_REVISION);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(wm831x->dev, "Failed to read revision: %d\n", ret);
|
dev_err(wm831x->dev, "Failed to read revision: %d\n", ret);
|
||||||
goto err;
|
goto err_regmap;
|
||||||
}
|
}
|
||||||
rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT;
|
rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT;
|
||||||
|
|
||||||
ret = wm831x_reg_read(wm831x, WM831X_RESET_ID);
|
ret = wm831x_reg_read(wm831x, WM831X_RESET_ID);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret);
|
dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret);
|
||||||
goto err;
|
goto err_regmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Some engineering samples do not have the ID set, rely on
|
/* Some engineering samples do not have the ID set, rely on
|
||||||
|
@ -1406,7 +1726,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||||
default:
|
default:
|
||||||
dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
|
dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto err;
|
goto err_regmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This will need revisiting in future but is OK for all
|
/* This will need revisiting in future but is OK for all
|
||||||
|
@ -1420,7 +1740,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||||
ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
|
ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(wm831x->dev, "Failed to read security key: %d\n", ret);
|
dev_err(wm831x->dev, "Failed to read security key: %d\n", ret);
|
||||||
goto err;
|
goto err_regmap;
|
||||||
}
|
}
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_warn(wm831x->dev, "Security key had non-zero value %x\n",
|
dev_warn(wm831x->dev, "Security key had non-zero value %x\n",
|
||||||
|
@ -1433,7 +1753,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||||
ret = pdata->pre_init(wm831x);
|
ret = pdata->pre_init(wm831x);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(wm831x->dev, "pre_init() failed: %d\n", ret);
|
dev_err(wm831x->dev, "pre_init() failed: %d\n", ret);
|
||||||
goto err;
|
goto err_regmap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1456,7 +1776,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||||
|
|
||||||
ret = wm831x_irq_init(wm831x, irq);
|
ret = wm831x_irq_init(wm831x, irq);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
goto err;
|
goto err_regmap;
|
||||||
|
|
||||||
wm831x_auxadc_init(wm831x);
|
wm831x_auxadc_init(wm831x);
|
||||||
|
|
||||||
|
@ -1552,8 +1872,9 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||||
|
|
||||||
err_irq:
|
err_irq:
|
||||||
wm831x_irq_exit(wm831x);
|
wm831x_irq_exit(wm831x);
|
||||||
err:
|
err_regmap:
|
||||||
mfd_remove_devices(wm831x->dev);
|
mfd_remove_devices(wm831x->dev);
|
||||||
|
regmap_exit(wm831x->regmap);
|
||||||
kfree(wm831x);
|
kfree(wm831x);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1565,6 +1886,7 @@ void wm831x_device_exit(struct wm831x *wm831x)
|
||||||
if (wm831x->irq_base)
|
if (wm831x->irq_base)
|
||||||
free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
|
free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
|
||||||
wm831x_irq_exit(wm831x);
|
wm831x_irq_exit(wm831x);
|
||||||
|
regmap_exit(wm831x->regmap);
|
||||||
kfree(wm831x);
|
kfree(wm831x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1604,6 +1926,15 @@ int wm831x_device_suspend(struct wm831x *wm831x)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wm831x_device_shutdown(struct wm831x *wm831x)
|
||||||
|
{
|
||||||
|
if (wm831x->soft_shutdown) {
|
||||||
|
dev_info(wm831x->dev, "Initiating shutdown...\n");
|
||||||
|
wm831x_set_bits(wm831x, WM831X_POWER_STATE, WM831X_CHIP_ON, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(wm831x_device_shutdown);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
|
MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("Mark Brown");
|
MODULE_AUTHOR("Mark Brown");
|
||||||
|
|
|
@ -18,67 +18,17 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
#include <linux/mfd/wm831x/core.h>
|
#include <linux/mfd/wm831x/core.h>
|
||||||
#include <linux/mfd/wm831x/pdata.h>
|
#include <linux/mfd/wm831x/pdata.h>
|
||||||
|
|
||||||
static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
|
|
||||||
int bytes, void *dest)
|
|
||||||
{
|
|
||||||
struct i2c_client *i2c = wm831x->control_data;
|
|
||||||
int ret;
|
|
||||||
u16 r = cpu_to_be16(reg);
|
|
||||||
|
|
||||||
ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
if (ret != 2)
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
ret = i2c_master_recv(i2c, dest, bytes);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
if (ret != bytes)
|
|
||||||
return -EIO;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Currently we allocate the write buffer on the stack; this is OK for
|
|
||||||
* small writes - if we need to do large writes this will need to be
|
|
||||||
* revised.
|
|
||||||
*/
|
|
||||||
static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
|
|
||||||
int bytes, void *src)
|
|
||||||
{
|
|
||||||
struct i2c_client *i2c = wm831x->control_data;
|
|
||||||
struct i2c_msg xfer[2];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
reg = cpu_to_be16(reg);
|
|
||||||
|
|
||||||
xfer[0].addr = i2c->addr;
|
|
||||||
xfer[0].flags = 0;
|
|
||||||
xfer[0].len = 2;
|
|
||||||
xfer[0].buf = (char *)®
|
|
||||||
|
|
||||||
xfer[1].addr = i2c->addr;
|
|
||||||
xfer[1].flags = I2C_M_NOSTART;
|
|
||||||
xfer[1].len = bytes;
|
|
||||||
xfer[1].buf = (char *)src;
|
|
||||||
|
|
||||||
ret = i2c_transfer(i2c->adapter, xfer, 2);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
if (ret != 2)
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wm831x_i2c_probe(struct i2c_client *i2c,
|
static int wm831x_i2c_probe(struct i2c_client *i2c,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct wm831x *wm831x;
|
struct wm831x *wm831x;
|
||||||
|
int ret;
|
||||||
|
|
||||||
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
|
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
|
||||||
if (wm831x == NULL)
|
if (wm831x == NULL)
|
||||||
|
@ -86,9 +36,15 @@ static int wm831x_i2c_probe(struct i2c_client *i2c,
|
||||||
|
|
||||||
i2c_set_clientdata(i2c, wm831x);
|
i2c_set_clientdata(i2c, wm831x);
|
||||||
wm831x->dev = &i2c->dev;
|
wm831x->dev = &i2c->dev;
|
||||||
wm831x->control_data = i2c;
|
|
||||||
wm831x->read_dev = wm831x_i2c_read_device;
|
wm831x->regmap = regmap_init_i2c(i2c, &wm831x_regmap_config);
|
||||||
wm831x->write_dev = wm831x_i2c_write_device;
|
if (IS_ERR(wm831x->regmap)) {
|
||||||
|
ret = PTR_ERR(wm831x->regmap);
|
||||||
|
dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
|
||||||
|
ret);
|
||||||
|
kfree(wm831x);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
|
return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
|
||||||
}
|
}
|
||||||
|
@ -109,6 +65,13 @@ static int wm831x_i2c_suspend(struct device *dev)
|
||||||
return wm831x_device_suspend(wm831x);
|
return wm831x_device_suspend(wm831x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wm831x_i2c_shutdown(struct i2c_client *i2c)
|
||||||
|
{
|
||||||
|
struct wm831x *wm831x = i2c_get_clientdata(i2c);
|
||||||
|
|
||||||
|
wm831x_device_shutdown(wm831x);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id wm831x_i2c_id[] = {
|
static const struct i2c_device_id wm831x_i2c_id[] = {
|
||||||
{ "wm8310", WM8310 },
|
{ "wm8310", WM8310 },
|
||||||
{ "wm8311", WM8311 },
|
{ "wm8311", WM8311 },
|
||||||
|
@ -133,6 +96,7 @@ static struct i2c_driver wm831x_i2c_driver = {
|
||||||
},
|
},
|
||||||
.probe = wm831x_i2c_probe,
|
.probe = wm831x_i2c_probe,
|
||||||
.remove = wm831x_i2c_remove,
|
.remove = wm831x_i2c_remove,
|
||||||
|
.shutdown = wm831x_i2c_shutdown,
|
||||||
.id_table = wm831x_i2c_id,
|
.id_table = wm831x_i2c_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,78 +16,19 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
|
||||||
#include <linux/mfd/wm831x/core.h>
|
#include <linux/mfd/wm831x/core.h>
|
||||||
|
|
||||||
static int wm831x_spi_read_device(struct wm831x *wm831x, unsigned short reg,
|
|
||||||
int bytes, void *dest)
|
|
||||||
{
|
|
||||||
u16 tx_val;
|
|
||||||
u16 *d = dest;
|
|
||||||
int r, ret;
|
|
||||||
|
|
||||||
/* Go register at a time */
|
|
||||||
for (r = reg; r < reg + (bytes / 2); r++) {
|
|
||||||
tx_val = r | 0x8000;
|
|
||||||
|
|
||||||
ret = spi_write_then_read(wm831x->control_data,
|
|
||||||
(u8 *)&tx_val, 2, (u8 *)d, 2);
|
|
||||||
if (ret != 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
*d = be16_to_cpu(*d);
|
|
||||||
|
|
||||||
d++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wm831x_spi_write_device(struct wm831x *wm831x, unsigned short reg,
|
|
||||||
int bytes, void *src)
|
|
||||||
{
|
|
||||||
struct spi_device *spi = wm831x->control_data;
|
|
||||||
u16 *s = src;
|
|
||||||
u16 data[2];
|
|
||||||
int ret, r;
|
|
||||||
|
|
||||||
/* Go register at a time */
|
|
||||||
for (r = reg; r < reg + (bytes / 2); r++) {
|
|
||||||
data[0] = r;
|
|
||||||
data[1] = *s++;
|
|
||||||
|
|
||||||
ret = spi_write(spi, (char *)&data, sizeof(data));
|
|
||||||
if (ret != 0)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __devinit wm831x_spi_probe(struct spi_device *spi)
|
static int __devinit wm831x_spi_probe(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
|
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||||
struct wm831x *wm831x;
|
struct wm831x *wm831x;
|
||||||
enum wm831x_parent type;
|
enum wm831x_parent type;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* Currently SPI support for ID tables is unmerged, we're faking it */
|
type = (enum wm831x_parent)id->driver_data;
|
||||||
if (strcmp(spi->modalias, "wm8310") == 0)
|
|
||||||
type = WM8310;
|
|
||||||
else if (strcmp(spi->modalias, "wm8311") == 0)
|
|
||||||
type = WM8311;
|
|
||||||
else if (strcmp(spi->modalias, "wm8312") == 0)
|
|
||||||
type = WM8312;
|
|
||||||
else if (strcmp(spi->modalias, "wm8320") == 0)
|
|
||||||
type = WM8320;
|
|
||||||
else if (strcmp(spi->modalias, "wm8321") == 0)
|
|
||||||
type = WM8321;
|
|
||||||
else if (strcmp(spi->modalias, "wm8325") == 0)
|
|
||||||
type = WM8325;
|
|
||||||
else if (strcmp(spi->modalias, "wm8326") == 0)
|
|
||||||
type = WM8326;
|
|
||||||
else {
|
|
||||||
dev_err(&spi->dev, "Unknown device type\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
|
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
|
||||||
if (wm831x == NULL)
|
if (wm831x == NULL)
|
||||||
|
@ -98,9 +39,15 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi)
|
||||||
|
|
||||||
dev_set_drvdata(&spi->dev, wm831x);
|
dev_set_drvdata(&spi->dev, wm831x);
|
||||||
wm831x->dev = &spi->dev;
|
wm831x->dev = &spi->dev;
|
||||||
wm831x->control_data = spi;
|
|
||||||
wm831x->read_dev = wm831x_spi_read_device;
|
wm831x->regmap = regmap_init_spi(spi, &wm831x_regmap_config);
|
||||||
wm831x->write_dev = wm831x_spi_write_device;
|
if (IS_ERR(wm831x->regmap)) {
|
||||||
|
ret = PTR_ERR(wm831x->regmap);
|
||||||
|
dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
|
||||||
|
ret);
|
||||||
|
kfree(wm831x);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return wm831x_device_init(wm831x, type, spi->irq);
|
return wm831x_device_init(wm831x, type, spi->irq);
|
||||||
}
|
}
|
||||||
|
@ -121,119 +68,50 @@ static int wm831x_spi_suspend(struct device *dev)
|
||||||
return wm831x_device_suspend(wm831x);
|
return wm831x_device_suspend(wm831x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wm831x_spi_shutdown(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
|
||||||
|
|
||||||
|
wm831x_device_shutdown(wm831x);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct dev_pm_ops wm831x_spi_pm = {
|
static const struct dev_pm_ops wm831x_spi_pm = {
|
||||||
.freeze = wm831x_spi_suspend,
|
.freeze = wm831x_spi_suspend,
|
||||||
.suspend = wm831x_spi_suspend,
|
.suspend = wm831x_spi_suspend,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spi_driver wm8310_spi_driver = {
|
static const struct spi_device_id wm831x_spi_ids[] = {
|
||||||
.driver = {
|
{ "wm8310", WM8310 },
|
||||||
.name = "wm8310",
|
{ "wm8311", WM8311 },
|
||||||
.bus = &spi_bus_type,
|
{ "wm8312", WM8312 },
|
||||||
.owner = THIS_MODULE,
|
{ "wm8320", WM8320 },
|
||||||
.pm = &wm831x_spi_pm,
|
{ "wm8321", WM8321 },
|
||||||
},
|
{ "wm8325", WM8325 },
|
||||||
.probe = wm831x_spi_probe,
|
{ "wm8326", WM8326 },
|
||||||
.remove = __devexit_p(wm831x_spi_remove),
|
{ },
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(spi, wm831x_spi_id);
|
||||||
|
|
||||||
static struct spi_driver wm8311_spi_driver = {
|
static struct spi_driver wm831x_spi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "wm8311",
|
.name = "wm831x",
|
||||||
.bus = &spi_bus_type,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = &wm831x_spi_pm,
|
|
||||||
},
|
|
||||||
.probe = wm831x_spi_probe,
|
|
||||||
.remove = __devexit_p(wm831x_spi_remove),
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct spi_driver wm8312_spi_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "wm8312",
|
|
||||||
.bus = &spi_bus_type,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = &wm831x_spi_pm,
|
|
||||||
},
|
|
||||||
.probe = wm831x_spi_probe,
|
|
||||||
.remove = __devexit_p(wm831x_spi_remove),
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct spi_driver wm8320_spi_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "wm8320",
|
|
||||||
.bus = &spi_bus_type,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = &wm831x_spi_pm,
|
|
||||||
},
|
|
||||||
.probe = wm831x_spi_probe,
|
|
||||||
.remove = __devexit_p(wm831x_spi_remove),
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct spi_driver wm8321_spi_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "wm8321",
|
|
||||||
.bus = &spi_bus_type,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = &wm831x_spi_pm,
|
|
||||||
},
|
|
||||||
.probe = wm831x_spi_probe,
|
|
||||||
.remove = __devexit_p(wm831x_spi_remove),
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct spi_driver wm8325_spi_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "wm8325",
|
|
||||||
.bus = &spi_bus_type,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = &wm831x_spi_pm,
|
|
||||||
},
|
|
||||||
.probe = wm831x_spi_probe,
|
|
||||||
.remove = __devexit_p(wm831x_spi_remove),
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct spi_driver wm8326_spi_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "wm8326",
|
|
||||||
.bus = &spi_bus_type,
|
.bus = &spi_bus_type,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.pm = &wm831x_spi_pm,
|
.pm = &wm831x_spi_pm,
|
||||||
},
|
},
|
||||||
|
.id_table = wm831x_spi_ids,
|
||||||
.probe = wm831x_spi_probe,
|
.probe = wm831x_spi_probe,
|
||||||
.remove = __devexit_p(wm831x_spi_remove),
|
.remove = __devexit_p(wm831x_spi_remove),
|
||||||
|
.shutdown = wm831x_spi_shutdown,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init wm831x_spi_init(void)
|
static int __init wm831x_spi_init(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = spi_register_driver(&wm8310_spi_driver);
|
ret = spi_register_driver(&wm831x_spi_driver);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
pr_err("Failed to register WM8310 SPI driver: %d\n", ret);
|
pr_err("Failed to register WM831x SPI driver: %d\n", ret);
|
||||||
|
|
||||||
ret = spi_register_driver(&wm8311_spi_driver);
|
|
||||||
if (ret != 0)
|
|
||||||
pr_err("Failed to register WM8311 SPI driver: %d\n", ret);
|
|
||||||
|
|
||||||
ret = spi_register_driver(&wm8312_spi_driver);
|
|
||||||
if (ret != 0)
|
|
||||||
pr_err("Failed to register WM8312 SPI driver: %d\n", ret);
|
|
||||||
|
|
||||||
ret = spi_register_driver(&wm8320_spi_driver);
|
|
||||||
if (ret != 0)
|
|
||||||
pr_err("Failed to register WM8320 SPI driver: %d\n", ret);
|
|
||||||
|
|
||||||
ret = spi_register_driver(&wm8321_spi_driver);
|
|
||||||
if (ret != 0)
|
|
||||||
pr_err("Failed to register WM8321 SPI driver: %d\n", ret);
|
|
||||||
|
|
||||||
ret = spi_register_driver(&wm8325_spi_driver);
|
|
||||||
if (ret != 0)
|
|
||||||
pr_err("Failed to register WM8325 SPI driver: %d\n", ret);
|
|
||||||
|
|
||||||
ret = spi_register_driver(&wm8326_spi_driver);
|
|
||||||
if (ret != 0)
|
|
||||||
pr_err("Failed to register WM8326 SPI driver: %d\n", ret);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -241,13 +119,7 @@ subsys_initcall(wm831x_spi_init);
|
||||||
|
|
||||||
static void __exit wm831x_spi_exit(void)
|
static void __exit wm831x_spi_exit(void)
|
||||||
{
|
{
|
||||||
spi_unregister_driver(&wm8326_spi_driver);
|
spi_unregister_driver(&wm831x_spi_driver);
|
||||||
spi_unregister_driver(&wm8325_spi_driver);
|
|
||||||
spi_unregister_driver(&wm8321_spi_driver);
|
|
||||||
spi_unregister_driver(&wm8320_spi_driver);
|
|
||||||
spi_unregister_driver(&wm8312_spi_driver);
|
|
||||||
spi_unregister_driver(&wm8311_spi_driver);
|
|
||||||
spi_unregister_driver(&wm8310_spi_driver);
|
|
||||||
}
|
}
|
||||||
module_exit(wm831x_spi_exit);
|
module_exit(wm831x_spi_exit);
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/mfd/wm8400-private.h>
|
#include <linux/mfd/wm8400-private.h>
|
||||||
#include <linux/mfd/wm8400-audio.h>
|
#include <linux/mfd/wm8400-audio.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
|
@ -123,14 +125,9 @@ static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest)
|
||||||
/* If there are any volatile reads then read back the entire block */
|
/* If there are any volatile reads then read back the entire block */
|
||||||
for (i = reg; i < reg + num_regs; i++)
|
for (i = reg; i < reg + num_regs; i++)
|
||||||
if (reg_data[i].vol) {
|
if (reg_data[i].vol) {
|
||||||
ret = wm8400->read_dev(wm8400->io_data, reg,
|
ret = regmap_bulk_read(wm8400->regmap, reg, dest,
|
||||||
num_regs, dest);
|
num_regs);
|
||||||
if (ret != 0)
|
|
||||||
return ret;
|
return ret;
|
||||||
for (i = 0; i < num_regs; i++)
|
|
||||||
dest[i] = be16_to_cpu(dest[i]);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise use the cache */
|
/* Otherwise use the cache */
|
||||||
|
@ -149,13 +146,10 @@ static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
|
||||||
for (i = 0; i < num_regs; i++) {
|
for (i = 0; i < num_regs; i++) {
|
||||||
BUG_ON(!reg_data[reg + i].writable);
|
BUG_ON(!reg_data[reg + i].writable);
|
||||||
wm8400->reg_cache[reg + i] = src[i];
|
wm8400->reg_cache[reg + i] = src[i];
|
||||||
src[i] = cpu_to_be16(src[i]);
|
ret = regmap_write(wm8400->regmap, reg, src[i]);
|
||||||
}
|
|
||||||
|
|
||||||
/* Do the actual I/O */
|
|
||||||
ret = wm8400->write_dev(wm8400->io_data, reg, num_regs, src);
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return -EIO;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -270,14 +264,14 @@ static int wm8400_init(struct wm8400 *wm8400,
|
||||||
dev_set_drvdata(wm8400->dev, wm8400);
|
dev_set_drvdata(wm8400->dev, wm8400);
|
||||||
|
|
||||||
/* Check that this is actually a WM8400 */
|
/* Check that this is actually a WM8400 */
|
||||||
ret = wm8400->read_dev(wm8400->io_data, WM8400_RESET_ID, 1, ®);
|
ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, &i);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(wm8400->dev, "Chip ID register read failed\n");
|
dev_err(wm8400->dev, "Chip ID register read failed\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
if (be16_to_cpu(reg) != reg_data[WM8400_RESET_ID].default_val) {
|
if (i != reg_data[WM8400_RESET_ID].default_val) {
|
||||||
dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n",
|
dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n",
|
||||||
be16_to_cpu(reg));
|
reg);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,9 +279,8 @@ static int wm8400_init(struct wm8400 *wm8400,
|
||||||
* is a PMIC we can't reset it safely so initialise the register
|
* is a PMIC we can't reset it safely so initialise the register
|
||||||
* cache from the hardware.
|
* cache from the hardware.
|
||||||
*/
|
*/
|
||||||
ret = wm8400->read_dev(wm8400->io_data, 0,
|
ret = regmap_raw_read(wm8400->regmap, 0, wm8400->reg_cache,
|
||||||
ARRAY_SIZE(wm8400->reg_cache),
|
ARRAY_SIZE(wm8400->reg_cache));
|
||||||
wm8400->reg_cache);
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(wm8400->dev, "Register cache read failed\n");
|
dev_err(wm8400->dev, "Register cache read failed\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
@ -337,60 +330,13 @@ static void wm8400_release(struct wm8400 *wm8400)
|
||||||
mfd_remove_devices(wm8400->dev);
|
mfd_remove_devices(wm8400->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct regmap_config wm8400_regmap_config = {
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 16,
|
||||||
|
.max_register = WM8400_REGISTER_COUNT - 1,
|
||||||
|
};
|
||||||
|
|
||||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||||
static int wm8400_i2c_read(void *io_data, char reg, int count, u16 *dest)
|
|
||||||
{
|
|
||||||
struct i2c_client *i2c = io_data;
|
|
||||||
struct i2c_msg xfer[2];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Write register */
|
|
||||||
xfer[0].addr = i2c->addr;
|
|
||||||
xfer[0].flags = 0;
|
|
||||||
xfer[0].len = 1;
|
|
||||||
xfer[0].buf = ®
|
|
||||||
|
|
||||||
/* Read data */
|
|
||||||
xfer[1].addr = i2c->addr;
|
|
||||||
xfer[1].flags = I2C_M_RD;
|
|
||||||
xfer[1].len = count * sizeof(u16);
|
|
||||||
xfer[1].buf = (u8 *)dest;
|
|
||||||
|
|
||||||
ret = i2c_transfer(i2c->adapter, xfer, 2);
|
|
||||||
if (ret == 2)
|
|
||||||
ret = 0;
|
|
||||||
else if (ret >= 0)
|
|
||||||
ret = -EIO;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wm8400_i2c_write(void *io_data, char reg, int count, const u16 *src)
|
|
||||||
{
|
|
||||||
struct i2c_client *i2c = io_data;
|
|
||||||
u8 *msg;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* We add 1 byte for device register - ideally I2C would gather. */
|
|
||||||
msg = kmalloc((count * sizeof(u16)) + 1, GFP_KERNEL);
|
|
||||||
if (msg == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
msg[0] = reg;
|
|
||||||
memcpy(&msg[1], src, count * sizeof(u16));
|
|
||||||
|
|
||||||
ret = i2c_master_send(i2c, msg, (count * sizeof(u16)) + 1);
|
|
||||||
|
|
||||||
if (ret == (count * 2) + 1)
|
|
||||||
ret = 0;
|
|
||||||
else if (ret >= 0)
|
|
||||||
ret = -EIO;
|
|
||||||
|
|
||||||
kfree(msg);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wm8400_i2c_probe(struct i2c_client *i2c,
|
static int wm8400_i2c_probe(struct i2c_client *i2c,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -403,18 +349,23 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
wm8400->io_data = i2c;
|
wm8400->regmap = regmap_init_i2c(i2c, &wm8400_regmap_config);
|
||||||
wm8400->read_dev = wm8400_i2c_read;
|
if (IS_ERR(wm8400->regmap)) {
|
||||||
wm8400->write_dev = wm8400_i2c_write;
|
ret = PTR_ERR(wm8400->regmap);
|
||||||
|
goto struct_err;
|
||||||
|
}
|
||||||
|
|
||||||
wm8400->dev = &i2c->dev;
|
wm8400->dev = &i2c->dev;
|
||||||
i2c_set_clientdata(i2c, wm8400);
|
i2c_set_clientdata(i2c, wm8400);
|
||||||
|
|
||||||
ret = wm8400_init(wm8400, i2c->dev.platform_data);
|
ret = wm8400_init(wm8400, i2c->dev.platform_data);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
goto struct_err;
|
goto map_err;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
map_err:
|
||||||
|
regmap_exit(wm8400->regmap);
|
||||||
struct_err:
|
struct_err:
|
||||||
kfree(wm8400);
|
kfree(wm8400);
|
||||||
err:
|
err:
|
||||||
|
@ -426,6 +377,7 @@ static int wm8400_i2c_remove(struct i2c_client *i2c)
|
||||||
struct wm8400 *wm8400 = i2c_get_clientdata(i2c);
|
struct wm8400 *wm8400 = i2c_get_clientdata(i2c);
|
||||||
|
|
||||||
wm8400_release(wm8400);
|
wm8400_release(wm8400);
|
||||||
|
regmap_exit(wm8400->regmap);
|
||||||
kfree(wm8400);
|
kfree(wm8400);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/regulator/machine.h>
|
#include <linux/regulator/machine.h>
|
||||||
|
|
||||||
|
@ -29,22 +31,7 @@
|
||||||
static int wm8994_read(struct wm8994 *wm8994, unsigned short reg,
|
static int wm8994_read(struct wm8994 *wm8994, unsigned short reg,
|
||||||
int bytes, void *dest)
|
int bytes, void *dest)
|
||||||
{
|
{
|
||||||
int ret, i;
|
return regmap_raw_read(wm8994->regmap, reg, dest, bytes);
|
||||||
u16 *buf = dest;
|
|
||||||
|
|
||||||
BUG_ON(bytes % 2);
|
|
||||||
BUG_ON(bytes <= 0);
|
|
||||||
|
|
||||||
ret = wm8994->read_dev(wm8994, reg, bytes, dest);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
for (i = 0; i < bytes / 2; i++) {
|
|
||||||
dev_vdbg(wm8994->dev, "Read %04x from R%d(0x%x)\n",
|
|
||||||
be16_to_cpu(buf[i]), reg + i, reg + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,19 +42,15 @@ static int wm8994_read(struct wm8994 *wm8994, unsigned short reg,
|
||||||
*/
|
*/
|
||||||
int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg)
|
int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg)
|
||||||
{
|
{
|
||||||
unsigned short val;
|
unsigned int val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&wm8994->io_lock);
|
ret = regmap_read(wm8994->regmap, reg, &val);
|
||||||
|
|
||||||
ret = wm8994_read(wm8994, reg, 2, &val);
|
|
||||||
|
|
||||||
mutex_unlock(&wm8994->io_lock);
|
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
else
|
else
|
||||||
return be16_to_cpu(val);
|
return val;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(wm8994_reg_read);
|
EXPORT_SYMBOL_GPL(wm8994_reg_read);
|
||||||
|
|
||||||
|
@ -82,33 +65,13 @@ EXPORT_SYMBOL_GPL(wm8994_reg_read);
|
||||||
int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
|
int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
|
||||||
int count, u16 *buf)
|
int count, u16 *buf)
|
||||||
{
|
{
|
||||||
int ret;
|
return regmap_bulk_read(wm8994->regmap, reg, buf, count);
|
||||||
|
|
||||||
mutex_lock(&wm8994->io_lock);
|
|
||||||
|
|
||||||
ret = wm8994_read(wm8994, reg, count * 2, buf);
|
|
||||||
|
|
||||||
mutex_unlock(&wm8994->io_lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(wm8994_bulk_read);
|
|
||||||
|
|
||||||
static int wm8994_write(struct wm8994 *wm8994, unsigned short reg,
|
static int wm8994_write(struct wm8994 *wm8994, unsigned short reg,
|
||||||
int bytes, const void *src)
|
int bytes, const void *src)
|
||||||
{
|
{
|
||||||
const u16 *buf = src;
|
return regmap_raw_write(wm8994->regmap, reg, src, bytes);
|
||||||
int i;
|
|
||||||
|
|
||||||
BUG_ON(bytes % 2);
|
|
||||||
BUG_ON(bytes <= 0);
|
|
||||||
|
|
||||||
for (i = 0; i < bytes / 2; i++) {
|
|
||||||
dev_vdbg(wm8994->dev, "Write %04x to R%d(0x%x)\n",
|
|
||||||
be16_to_cpu(buf[i]), reg + i, reg + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return wm8994->write_dev(wm8994, reg, bytes, src);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,17 +84,7 @@ static int wm8994_write(struct wm8994 *wm8994, unsigned short reg,
|
||||||
int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
|
int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
|
||||||
unsigned short val)
|
unsigned short val)
|
||||||
{
|
{
|
||||||
int ret;
|
return regmap_write(wm8994->regmap, reg, val);
|
||||||
|
|
||||||
val = cpu_to_be16(val);
|
|
||||||
|
|
||||||
mutex_lock(&wm8994->io_lock);
|
|
||||||
|
|
||||||
ret = wm8994_write(wm8994, reg, 2, &val);
|
|
||||||
|
|
||||||
mutex_unlock(&wm8994->io_lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(wm8994_reg_write);
|
EXPORT_SYMBOL_GPL(wm8994_reg_write);
|
||||||
|
|
||||||
|
@ -146,15 +99,7 @@ EXPORT_SYMBOL_GPL(wm8994_reg_write);
|
||||||
int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg,
|
int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg,
|
||||||
int count, const u16 *buf)
|
int count, const u16 *buf)
|
||||||
{
|
{
|
||||||
int ret;
|
return regmap_raw_write(wm8994->regmap, reg, buf, count * sizeof(u16));
|
||||||
|
|
||||||
mutex_lock(&wm8994->io_lock);
|
|
||||||
|
|
||||||
ret = wm8994_write(wm8994, reg, count * 2, buf);
|
|
||||||
|
|
||||||
mutex_unlock(&wm8994->io_lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(wm8994_bulk_write);
|
EXPORT_SYMBOL_GPL(wm8994_bulk_write);
|
||||||
|
|
||||||
|
@ -169,28 +114,7 @@ EXPORT_SYMBOL_GPL(wm8994_bulk_write);
|
||||||
int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
|
int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
|
||||||
unsigned short mask, unsigned short val)
|
unsigned short mask, unsigned short val)
|
||||||
{
|
{
|
||||||
int ret;
|
return regmap_update_bits(wm8994->regmap, reg, mask, val);
|
||||||
u16 r;
|
|
||||||
|
|
||||||
mutex_lock(&wm8994->io_lock);
|
|
||||||
|
|
||||||
ret = wm8994_read(wm8994, reg, 2, &r);
|
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
r = be16_to_cpu(r);
|
|
||||||
|
|
||||||
r &= ~mask;
|
|
||||||
r |= val;
|
|
||||||
|
|
||||||
r = cpu_to_be16(r);
|
|
||||||
|
|
||||||
ret = wm8994_write(wm8994, reg, 2, &r);
|
|
||||||
|
|
||||||
out:
|
|
||||||
mutex_unlock(&wm8994->io_lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(wm8994_set_bits);
|
EXPORT_SYMBOL_GPL(wm8994_set_bits);
|
||||||
|
|
||||||
|
@ -378,6 +302,11 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static struct regmap_config wm8994_regmap_config = {
|
||||||
|
.reg_bits = 16,
|
||||||
|
.val_bits = 16,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Instantiate the generic non-control parts of the device.
|
* Instantiate the generic non-control parts of the device.
|
||||||
*/
|
*/
|
||||||
|
@ -387,7 +316,6 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
||||||
const char *devname;
|
const char *devname;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
mutex_init(&wm8994->io_lock);
|
|
||||||
dev_set_drvdata(wm8994->dev, wm8994);
|
dev_set_drvdata(wm8994->dev, wm8994);
|
||||||
|
|
||||||
/* Add the on-chip regulators first for bootstrapping */
|
/* Add the on-chip regulators first for bootstrapping */
|
||||||
|
@ -397,7 +325,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
||||||
NULL, 0);
|
NULL, 0);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
|
dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
|
||||||
goto err;
|
goto err_regmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (wm8994->type) {
|
switch (wm8994->type) {
|
||||||
|
@ -409,7 +337,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
goto err;
|
goto err_regmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
|
wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
|
||||||
|
@ -417,7 +345,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!wm8994->supplies) {
|
if (!wm8994->supplies) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err;
|
goto err_regmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (wm8994->type) {
|
switch (wm8994->type) {
|
||||||
|
@ -431,7 +359,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
goto err;
|
goto err_regmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
|
ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
|
||||||
|
@ -554,7 +482,8 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
||||||
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
|
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
|
||||||
err_supplies:
|
err_supplies:
|
||||||
kfree(wm8994->supplies);
|
kfree(wm8994->supplies);
|
||||||
err:
|
err_regmap:
|
||||||
|
regmap_exit(wm8994->regmap);
|
||||||
mfd_remove_devices(wm8994->dev);
|
mfd_remove_devices(wm8994->dev);
|
||||||
kfree(wm8994);
|
kfree(wm8994);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -569,62 +498,15 @@ static void wm8994_device_exit(struct wm8994 *wm8994)
|
||||||
wm8994->supplies);
|
wm8994->supplies);
|
||||||
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
|
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
|
||||||
kfree(wm8994->supplies);
|
kfree(wm8994->supplies);
|
||||||
|
regmap_exit(wm8994->regmap);
|
||||||
kfree(wm8994);
|
kfree(wm8994);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wm8994_i2c_read_device(struct wm8994 *wm8994, unsigned short reg,
|
|
||||||
int bytes, void *dest)
|
|
||||||
{
|
|
||||||
struct i2c_client *i2c = wm8994->control_data;
|
|
||||||
int ret;
|
|
||||||
u16 r = cpu_to_be16(reg);
|
|
||||||
|
|
||||||
ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
if (ret != 2)
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
ret = i2c_master_recv(i2c, dest, bytes);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
if (ret != bytes)
|
|
||||||
return -EIO;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg,
|
|
||||||
int bytes, const void *src)
|
|
||||||
{
|
|
||||||
struct i2c_client *i2c = wm8994->control_data;
|
|
||||||
struct i2c_msg xfer[2];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
reg = cpu_to_be16(reg);
|
|
||||||
|
|
||||||
xfer[0].addr = i2c->addr;
|
|
||||||
xfer[0].flags = 0;
|
|
||||||
xfer[0].len = 2;
|
|
||||||
xfer[0].buf = (char *)®
|
|
||||||
|
|
||||||
xfer[1].addr = i2c->addr;
|
|
||||||
xfer[1].flags = I2C_M_NOSTART;
|
|
||||||
xfer[1].len = bytes;
|
|
||||||
xfer[1].buf = (char *)src;
|
|
||||||
|
|
||||||
ret = i2c_transfer(i2c->adapter, xfer, 2);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
if (ret != 2)
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wm8994_i2c_probe(struct i2c_client *i2c,
|
static int wm8994_i2c_probe(struct i2c_client *i2c,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct wm8994 *wm8994;
|
struct wm8994 *wm8994;
|
||||||
|
int ret;
|
||||||
|
|
||||||
wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL);
|
wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL);
|
||||||
if (wm8994 == NULL)
|
if (wm8994 == NULL)
|
||||||
|
@ -632,12 +514,18 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
|
||||||
|
|
||||||
i2c_set_clientdata(i2c, wm8994);
|
i2c_set_clientdata(i2c, wm8994);
|
||||||
wm8994->dev = &i2c->dev;
|
wm8994->dev = &i2c->dev;
|
||||||
wm8994->control_data = i2c;
|
|
||||||
wm8994->read_dev = wm8994_i2c_read_device;
|
|
||||||
wm8994->write_dev = wm8994_i2c_write_device;
|
|
||||||
wm8994->irq = i2c->irq;
|
wm8994->irq = i2c->irq;
|
||||||
wm8994->type = id->driver_data;
|
wm8994->type = id->driver_data;
|
||||||
|
|
||||||
|
wm8994->regmap = regmap_init_i2c(i2c, &wm8994_regmap_config);
|
||||||
|
if (IS_ERR(wm8994->regmap)) {
|
||||||
|
ret = PTR_ERR(wm8994->regmap);
|
||||||
|
dev_err(wm8994->dev, "Failed to allocate register map: %d\n",
|
||||||
|
ret);
|
||||||
|
kfree(wm8994);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return wm8994_device_init(wm8994, i2c->irq);
|
return wm8994_device_init(wm8994, i2c->irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register values.
|
* Register values.
|
||||||
|
@ -361,12 +362,8 @@ struct wm831x {
|
||||||
struct mutex io_lock;
|
struct mutex io_lock;
|
||||||
|
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
int (*read_dev)(struct wm831x *wm831x, unsigned short reg,
|
|
||||||
int bytes, void *dest);
|
|
||||||
int (*write_dev)(struct wm831x *wm831x, unsigned short reg,
|
|
||||||
int bytes, void *src);
|
|
||||||
|
|
||||||
void *control_data;
|
struct regmap *regmap;
|
||||||
|
|
||||||
int irq; /* Our chip IRQ */
|
int irq; /* Our chip IRQ */
|
||||||
struct mutex irq_lock;
|
struct mutex irq_lock;
|
||||||
|
@ -374,6 +371,8 @@ struct wm831x {
|
||||||
int irq_masks_cur[WM831X_NUM_IRQ_REGS]; /* Currently active value */
|
int irq_masks_cur[WM831X_NUM_IRQ_REGS]; /* Currently active value */
|
||||||
int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
|
int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
|
||||||
|
|
||||||
|
bool soft_shutdown;
|
||||||
|
|
||||||
/* Chip revision based flags */
|
/* Chip revision based flags */
|
||||||
unsigned has_gpio_ena:1; /* Has GPIO enable bit */
|
unsigned has_gpio_ena:1; /* Has GPIO enable bit */
|
||||||
unsigned has_cs_sts:1; /* Has current sink status bit */
|
unsigned has_cs_sts:1; /* Has current sink status bit */
|
||||||
|
@ -412,8 +411,11 @@ int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
|
||||||
int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq);
|
int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq);
|
||||||
void wm831x_device_exit(struct wm831x *wm831x);
|
void wm831x_device_exit(struct wm831x *wm831x);
|
||||||
int wm831x_device_suspend(struct wm831x *wm831x);
|
int wm831x_device_suspend(struct wm831x *wm831x);
|
||||||
|
void wm831x_device_shutdown(struct wm831x *wm831x);
|
||||||
int wm831x_irq_init(struct wm831x *wm831x, int irq);
|
int wm831x_irq_init(struct wm831x *wm831x, int irq);
|
||||||
void wm831x_irq_exit(struct wm831x *wm831x);
|
void wm831x_irq_exit(struct wm831x *wm831x);
|
||||||
void wm831x_auxadc_init(struct wm831x *wm831x);
|
void wm831x_auxadc_init(struct wm831x *wm831x);
|
||||||
|
|
||||||
|
extern struct regmap_config wm831x_regmap_config;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -123,6 +123,9 @@ struct wm831x_pdata {
|
||||||
/** Disable the touchscreen */
|
/** Disable the touchscreen */
|
||||||
bool disable_touch;
|
bool disable_touch;
|
||||||
|
|
||||||
|
/** The driver should initiate a power off sequence during shutdown */
|
||||||
|
bool soft_shutdown;
|
||||||
|
|
||||||
int irq_base;
|
int irq_base;
|
||||||
int gpio_base;
|
int gpio_base;
|
||||||
int gpio_defaults[WM831X_GPIO_NUM];
|
int gpio_defaults[WM831X_GPIO_NUM];
|
||||||
|
|
|
@ -25,16 +25,15 @@
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
struct regmap;
|
||||||
|
|
||||||
#define WM8400_REGISTER_COUNT 0x55
|
#define WM8400_REGISTER_COUNT 0x55
|
||||||
|
|
||||||
struct wm8400 {
|
struct wm8400 {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
|
||||||
int (*read_dev)(void *data, char reg, int count, u16 *dst);
|
|
||||||
int (*write_dev)(void *data, char reg, int count, const u16 *src);
|
|
||||||
|
|
||||||
struct mutex io_lock;
|
struct mutex io_lock;
|
||||||
void *io_data;
|
struct regmap *regmap;
|
||||||
|
|
||||||
u16 reg_cache[WM8400_REGISTER_COUNT];
|
u16 reg_cache[WM8400_REGISTER_COUNT];
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ enum wm8994_type {
|
||||||
|
|
||||||
struct regulator_dev;
|
struct regulator_dev;
|
||||||
struct regulator_bulk_data;
|
struct regulator_bulk_data;
|
||||||
|
struct regmap;
|
||||||
|
|
||||||
#define WM8994_NUM_GPIO_REGS 11
|
#define WM8994_NUM_GPIO_REGS 11
|
||||||
#define WM8994_NUM_LDO_REGS 2
|
#define WM8994_NUM_LDO_REGS 2
|
||||||
|
@ -50,18 +51,12 @@ struct regulator_bulk_data;
|
||||||
#define WM8994_IRQ_GPIO(x) (x + WM8994_IRQ_TEMP_WARN)
|
#define WM8994_IRQ_GPIO(x) (x + WM8994_IRQ_TEMP_WARN)
|
||||||
|
|
||||||
struct wm8994 {
|
struct wm8994 {
|
||||||
struct mutex io_lock;
|
|
||||||
struct mutex irq_lock;
|
struct mutex irq_lock;
|
||||||
|
|
||||||
enum wm8994_type type;
|
enum wm8994_type type;
|
||||||
|
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
int (*read_dev)(struct wm8994 *wm8994, unsigned short reg,
|
struct regmap *regmap;
|
||||||
int bytes, void *dest);
|
|
||||||
int (*write_dev)(struct wm8994 *wm8994, unsigned short reg,
|
|
||||||
int bytes, const void *src);
|
|
||||||
|
|
||||||
void *control_data;
|
|
||||||
|
|
||||||
int gpio_base;
|
int gpio_base;
|
||||||
int irq_base;
|
int irq_base;
|
||||||
|
|
|
@ -20,9 +20,77 @@
|
||||||
struct i2c_client;
|
struct i2c_client;
|
||||||
struct spi_device;
|
struct spi_device;
|
||||||
|
|
||||||
|
/* An enum of all the supported cache types */
|
||||||
|
enum regcache_type {
|
||||||
|
REGCACHE_NONE,
|
||||||
|
REGCACHE_INDEXED,
|
||||||
|
REGCACHE_RBTREE,
|
||||||
|
REGCACHE_LZO
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for a register. We use an array of structs rather
|
||||||
|
* than a simple array as many modern devices have very sparse
|
||||||
|
* register maps.
|
||||||
|
*
|
||||||
|
* @reg: Register address.
|
||||||
|
* @def: Register default value.
|
||||||
|
*/
|
||||||
|
struct reg_default {
|
||||||
|
unsigned int reg;
|
||||||
|
unsigned int def;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for the register map of a device.
|
||||||
|
*
|
||||||
|
* @reg_bits: Number of bits in a register address, mandatory.
|
||||||
|
* @val_bits: Number of bits in a register value, mandatory.
|
||||||
|
*
|
||||||
|
* @writeable_reg: Optional callback returning true if the register
|
||||||
|
* can be written to.
|
||||||
|
* @readable_reg: Optional callback returning true if the register
|
||||||
|
* can be read from.
|
||||||
|
* @volatile_reg: Optional callback returning true if the register
|
||||||
|
* value can't be cached.
|
||||||
|
* @precious_reg: Optional callback returning true if the rgister
|
||||||
|
* should not be read outside of a call from the driver
|
||||||
|
* (eg, a clear on read interrupt status register).
|
||||||
|
*
|
||||||
|
* @max_register: Optional, specifies the maximum valid register index.
|
||||||
|
* @reg_defaults: Power on reset values for registers (for use with
|
||||||
|
* register cache support).
|
||||||
|
* @num_reg_defaults: Number of elements in reg_defaults.
|
||||||
|
*
|
||||||
|
* @read_flag_mask: Mask to be set in the top byte of the register when doing
|
||||||
|
* a read.
|
||||||
|
* @write_flag_mask: Mask to be set in the top byte of the register when doing
|
||||||
|
* a write. If both read_flag_mask and write_flag_mask are
|
||||||
|
* empty the regmap_bus default masks are used.
|
||||||
|
*
|
||||||
|
* @cache_type: The actual cache type.
|
||||||
|
* @reg_defaults_raw: Power on reset values for registers (for use with
|
||||||
|
* register cache support).
|
||||||
|
* @num_reg_defaults_raw: Number of elements in reg_defaults_raw.
|
||||||
|
*/
|
||||||
struct regmap_config {
|
struct regmap_config {
|
||||||
int reg_bits;
|
int reg_bits;
|
||||||
int val_bits;
|
int val_bits;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
unsigned int max_register;
|
||||||
|
struct reg_default *reg_defaults;
|
||||||
|
unsigned int num_reg_defaults;
|
||||||
|
enum regcache_type cache_type;
|
||||||
|
const void *reg_defaults_raw;
|
||||||
|
unsigned int num_reg_defaults_raw;
|
||||||
|
|
||||||
|
u8 read_flag_mask;
|
||||||
|
u8 write_flag_mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int (*regmap_hw_write)(struct device *dev, const void *data,
|
typedef int (*regmap_hw_write)(struct device *dev, const void *data,
|
||||||
|
@ -37,25 +105,18 @@ typedef int (*regmap_hw_read)(struct device *dev,
|
||||||
/**
|
/**
|
||||||
* Description of a hardware bus for the register map infrastructure.
|
* Description of a hardware bus for the register map infrastructure.
|
||||||
*
|
*
|
||||||
* @list: Internal use.
|
|
||||||
* @type: Bus type, used to identify bus to be used for a device.
|
|
||||||
* @write: Write operation.
|
* @write: Write operation.
|
||||||
* @gather_write: Write operation with split register/value, return -ENOTSUPP
|
* @gather_write: Write operation with split register/value, return -ENOTSUPP
|
||||||
* if not implemented on a given device.
|
* if not implemented on a given device.
|
||||||
* @read: Read operation. Data is returned in the buffer used to transmit
|
* @read: Read operation. Data is returned in the buffer used to transmit
|
||||||
* data.
|
* data.
|
||||||
* @owner: Module with the bus implementation, used to pin the implementation
|
|
||||||
* in memory.
|
|
||||||
* @read_flag_mask: Mask to be set in the top byte of the register when doing
|
* @read_flag_mask: Mask to be set in the top byte of the register when doing
|
||||||
* a read.
|
* a read.
|
||||||
*/
|
*/
|
||||||
struct regmap_bus {
|
struct regmap_bus {
|
||||||
struct list_head list;
|
|
||||||
struct bus_type *type;
|
|
||||||
regmap_hw_write write;
|
regmap_hw_write write;
|
||||||
regmap_hw_gather_write gather_write;
|
regmap_hw_gather_write gather_write;
|
||||||
regmap_hw_read read;
|
regmap_hw_read read;
|
||||||
struct module *owner;
|
|
||||||
u8 read_flag_mask;
|
u8 read_flag_mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,4 +140,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
|
||||||
int regmap_update_bits(struct regmap *map, unsigned int reg,
|
int regmap_update_bits(struct regmap *map, unsigned int reg,
|
||||||
unsigned int mask, unsigned int val);
|
unsigned int mask, unsigned int val);
|
||||||
|
|
||||||
|
int regcache_sync(struct regmap *map);
|
||||||
|
void regcache_cache_only(struct regmap *map, bool enable);
|
||||||
|
void regcache_cache_bypass(struct regmap *map, bool enable);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
136
include/trace/events/regmap.h
Normal file
136
include/trace/events/regmap.h
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
#undef TRACE_SYSTEM
|
||||||
|
#define TRACE_SYSTEM regmap
|
||||||
|
|
||||||
|
#if !defined(_TRACE_REGMAP_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||||
|
#define _TRACE_REGMAP_H
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/ktime.h>
|
||||||
|
#include <linux/tracepoint.h>
|
||||||
|
|
||||||
|
struct regmap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Log register events
|
||||||
|
*/
|
||||||
|
DECLARE_EVENT_CLASS(regmap_reg,
|
||||||
|
|
||||||
|
TP_PROTO(struct device *dev, unsigned int reg,
|
||||||
|
unsigned int val),
|
||||||
|
|
||||||
|
TP_ARGS(dev, reg, val),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__string( name, dev_name(dev) )
|
||||||
|
__field( unsigned int, reg )
|
||||||
|
__field( unsigned int, val )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__assign_str(name, dev_name(dev));
|
||||||
|
__entry->reg = reg;
|
||||||
|
__entry->val = val;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("%s reg=%x val=%x", __get_str(name),
|
||||||
|
(unsigned int)__entry->reg,
|
||||||
|
(unsigned int)__entry->val)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(regmap_reg, regmap_reg_write,
|
||||||
|
|
||||||
|
TP_PROTO(struct device *dev, unsigned int reg,
|
||||||
|
unsigned int val),
|
||||||
|
|
||||||
|
TP_ARGS(dev, reg, val)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(regmap_reg, regmap_reg_read,
|
||||||
|
|
||||||
|
TP_PROTO(struct device *dev, unsigned int reg,
|
||||||
|
unsigned int val),
|
||||||
|
|
||||||
|
TP_ARGS(dev, reg, val)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
DECLARE_EVENT_CLASS(regmap_block,
|
||||||
|
|
||||||
|
TP_PROTO(struct device *dev, unsigned int reg, int count),
|
||||||
|
|
||||||
|
TP_ARGS(dev, reg, count),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__string( name, dev_name(dev) )
|
||||||
|
__field( unsigned int, reg )
|
||||||
|
__field( int, count )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__assign_str(name, dev_name(dev));
|
||||||
|
__entry->reg = reg;
|
||||||
|
__entry->count = count;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("%s reg=%x count=%d", __get_str(name),
|
||||||
|
(unsigned int)__entry->reg,
|
||||||
|
(int)__entry->count)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(regmap_block, regmap_hw_read_start,
|
||||||
|
|
||||||
|
TP_PROTO(struct device *dev, unsigned int reg, int count),
|
||||||
|
|
||||||
|
TP_ARGS(dev, reg, count)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(regmap_block, regmap_hw_read_done,
|
||||||
|
|
||||||
|
TP_PROTO(struct device *dev, unsigned int reg, int count),
|
||||||
|
|
||||||
|
TP_ARGS(dev, reg, count)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(regmap_block, regmap_hw_write_start,
|
||||||
|
|
||||||
|
TP_PROTO(struct device *dev, unsigned int reg, int count),
|
||||||
|
|
||||||
|
TP_ARGS(dev, reg, count)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(regmap_block, regmap_hw_write_done,
|
||||||
|
|
||||||
|
TP_PROTO(struct device *dev, unsigned int reg, int count),
|
||||||
|
|
||||||
|
TP_ARGS(dev, reg, count)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(regcache_sync,
|
||||||
|
|
||||||
|
TP_PROTO(struct device *dev, const char *type,
|
||||||
|
const char *status),
|
||||||
|
|
||||||
|
TP_ARGS(dev, type, status),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__string( name, dev_name(dev) )
|
||||||
|
__string( status, status )
|
||||||
|
__string( type, type )
|
||||||
|
__field( int, type )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__assign_str(name, dev_name(dev));
|
||||||
|
__assign_str(status, status);
|
||||||
|
__assign_str(type, type);
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("%s type=%s status=%s", __get_str(name),
|
||||||
|
__get_str(type), __get_str(status))
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif /* _TRACE_REGMAP_H */
|
||||||
|
|
||||||
|
/* This part must be outside protection */
|
||||||
|
#include <trace/define_trace.h>
|
Loading…
Reference in a new issue