mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-13 08:39:52 +00:00
regmap: Updates for v3.16
Another fairly quiet release, a few bug fixes and a couple of new features: - Support for I2C devices connected to SMBus rather than full I2C controllers contributed by Boris Brezillon. If the controller is only capable of SMBus operation the framework will transparently fall back to that. - Suport for little endian values, contributed by Xiubo Li. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTjaBoAAoJELSic+t+oim9lz4P/jcFjELCezVmIZVmnz67SJeq yAJebbSaY2i5foXxxpBjUhUucTN3+7cPanBfzq5S6Go7pwVhzo+BhgK1QBteSg3d 3Sl6LW6DJIpIJDBnZ0JWpg/Q4qu8s0bh/IlFpTXhlLJI08LOrc2/qB/uOZM51cBk loXKymmMK27JxzUbrHTVDX+GLgwyHOI2f9jG2kJuv9LON4BrqjPzBQZisFxIpxq4 rf/obn2xTWW5eXGavF9geVf9kcHr8Xvvcz+HSVr7VdzuAznp7kovDfMtsdJW5rJG 0q2w60k3qcjdffjdAe4v5EP8qKHKykeJq+JsyLe7uqL2xhxWyZS0E66Gpe1nl/8O jR0zNzjL6BfwZbNgNA+YksaavnpMQLSHinIzEgur3C25Iq2kXKsCgys+l09qcbws dNCHu9i30Wew3B1EU6eh1M2F3WKrIAqELbByjbTeZXSr7OxYuKzF96fmg6ta5gjk 4kf6jsWJPYHqxQZXV3VxvKy6y5Au/XapVgJ1HxmAX6D2jC51xQQeNN8ViYtmgCKA jPup3JRtGT322S7Rlwl0Fg36Whbz/H9HTbJFqclDna/z2dsSfJBVef8QFEazVSFa BiBlGFgvf3XtTUWsUJJyY8PveqbrgbS8/dkGyMpxiFdKp5Z/ki3Lh5+QPdgYmrgk h73oV5jRCEtQnOfKW9Jl =XMnS -----END PGP SIGNATURE----- Merge tag 'regmap-v3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap into next Pull regmap updates from Mark Brown: "Another fairly quiet release, a few bug fixes and a couple of new features: - support for I2C devices connected to SMBus rather than full I2C controllers contributed by Boris Brezillon. If the controller is only capable of SMBus operation the framework will transparently fall back to that - suport for little endian values, contributed by Xiubo Li" * tag 'regmap-v3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: regmap: mmio: Fix regmap_mmio_write for uneven counts regmap: irq: Fix possible ZERO_SIZE_PTR pointer dereferencing error. regmap: Add missing initialization of this_page regmap: Fix possible ZERO_SIZE_PTR pointer dereferencing error. regmap: i2c: fallback to SMBus if the adapter does not support standard I2C regmap: add reg_read/reg_write callbacks to regmap_bus struct regmap: rbtree: improve 64bits memory alignment regmap: mmio: Fix the bug of 'offset' value parsing. regmap: implement LE formatting/parsing for 16/32-bit values.
This commit is contained in:
commit
de6b25de45
@ -23,16 +23,16 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
|
||||
static int regcache_rbtree_exit(struct regmap *map);
|
||||
|
||||
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;
|
||||
/* Which registers are present */
|
||||
long *cache_present;
|
||||
/* base register handled by this block */
|
||||
unsigned int base_reg;
|
||||
/* number of registers available in the block */
|
||||
unsigned int blklen;
|
||||
/* the actual rbtree node holding this block */
|
||||
struct rb_node node;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct regcache_rbtree_ctx {
|
||||
|
@ -14,6 +14,79 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
|
||||
static int regmap_smbus_byte_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
int ret;
|
||||
|
||||
if (reg > 0xff)
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(i2c, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regmap_smbus_byte_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
|
||||
if (val > 0xff || reg > 0xff)
|
||||
return -EINVAL;
|
||||
|
||||
return i2c_smbus_write_byte_data(i2c, reg, val);
|
||||
}
|
||||
|
||||
static struct regmap_bus regmap_smbus_byte = {
|
||||
.reg_write = regmap_smbus_byte_reg_write,
|
||||
.reg_read = regmap_smbus_byte_reg_read,
|
||||
};
|
||||
|
||||
static int regmap_smbus_word_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
int ret;
|
||||
|
||||
if (reg > 0xff)
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_read_word_data(i2c, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regmap_smbus_word_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
|
||||
if (val > 0xffff || reg > 0xff)
|
||||
return -EINVAL;
|
||||
|
||||
return i2c_smbus_write_word_data(i2c, reg, val);
|
||||
}
|
||||
|
||||
static struct regmap_bus regmap_smbus_word = {
|
||||
.reg_write = regmap_smbus_word_reg_write,
|
||||
.reg_read = regmap_smbus_word_reg_read,
|
||||
};
|
||||
|
||||
static int regmap_i2c_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
struct device *dev = context;
|
||||
@ -97,6 +170,23 @@ static struct regmap_bus regmap_i2c = {
|
||||
.read = regmap_i2c_read,
|
||||
};
|
||||
|
||||
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
|
||||
return ®map_i2c;
|
||||
else if (config->val_bits == 16 && config->reg_bits == 8 &&
|
||||
i2c_check_functionality(i2c->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return ®map_smbus_word;
|
||||
else if (config->val_bits == 8 && config->reg_bits == 8 &&
|
||||
i2c_check_functionality(i2c->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return ®map_smbus_byte;
|
||||
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
/**
|
||||
* regmap_init_i2c(): Initialise register map
|
||||
*
|
||||
@ -109,7 +199,12 @@ static struct regmap_bus regmap_i2c = {
|
||||
struct regmap *regmap_init_i2c(struct i2c_client *i2c,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config);
|
||||
const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
|
||||
|
||||
if (IS_ERR(bus))
|
||||
return ERR_CAST(bus);
|
||||
|
||||
return regmap_init(&i2c->dev, bus, &i2c->dev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_init_i2c);
|
||||
|
||||
@ -126,7 +221,12 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c);
|
||||
struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return devm_regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config);
|
||||
const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
|
||||
|
||||
if (IS_ERR(bus))
|
||||
return ERR_CAST(bus);
|
||||
|
||||
return devm_regmap_init(&i2c->dev, bus, &i2c->dev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
|
||||
|
||||
|
@ -10,13 +10,13 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "internal.h"
|
||||
@ -347,6 +347,9 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
||||
int ret = -ENOMEM;
|
||||
u32 reg;
|
||||
|
||||
if (chip->num_regs <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < chip->num_irqs; i++) {
|
||||
if (chip->irqs[i].reg_offset % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
@ -61,9 +61,28 @@ static int regmap_mmio_regbits_check(size_t reg_bits)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void regmap_mmio_count_check(size_t count)
|
||||
static inline void regmap_mmio_count_check(size_t count, u32 offset)
|
||||
{
|
||||
BUG_ON(count % 2 != 0);
|
||||
BUG_ON(count <= offset);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
regmap_mmio_get_offset(const void *reg, size_t reg_size)
|
||||
{
|
||||
switch (reg_size) {
|
||||
case 1:
|
||||
return *(u8 *)reg;
|
||||
case 2:
|
||||
return *(u16 *)reg;
|
||||
case 4:
|
||||
return *(u32 *)reg;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 8:
|
||||
return *(u64 *)reg;
|
||||
#endif
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static int regmap_mmio_gather_write(void *context,
|
||||
@ -71,7 +90,7 @@ static int regmap_mmio_gather_write(void *context,
|
||||
const void *val, size_t val_size)
|
||||
{
|
||||
struct regmap_mmio_context *ctx = context;
|
||||
u32 offset;
|
||||
unsigned int offset;
|
||||
int ret;
|
||||
|
||||
regmap_mmio_regsize_check(reg_size);
|
||||
@ -82,7 +101,7 @@ static int regmap_mmio_gather_write(void *context,
|
||||
return ret;
|
||||
}
|
||||
|
||||
offset = *(u32 *)reg;
|
||||
offset = regmap_mmio_get_offset(reg, reg_size);
|
||||
|
||||
while (val_size) {
|
||||
switch (ctx->val_bytes) {
|
||||
@ -118,9 +137,9 @@ static int regmap_mmio_gather_write(void *context,
|
||||
static int regmap_mmio_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
struct regmap_mmio_context *ctx = context;
|
||||
u32 offset = ctx->reg_bytes + ctx->pad_bytes;
|
||||
unsigned int offset = ctx->reg_bytes + ctx->pad_bytes;
|
||||
|
||||
regmap_mmio_count_check(count);
|
||||
regmap_mmio_count_check(count, offset);
|
||||
|
||||
return regmap_mmio_gather_write(context, data, ctx->reg_bytes,
|
||||
data + offset, count - offset);
|
||||
@ -131,7 +150,7 @@ static int regmap_mmio_read(void *context,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
struct regmap_mmio_context *ctx = context;
|
||||
u32 offset;
|
||||
unsigned int offset;
|
||||
int ret;
|
||||
|
||||
regmap_mmio_regsize_check(reg_size);
|
||||
@ -142,7 +161,7 @@ static int regmap_mmio_read(void *context,
|
||||
return ret;
|
||||
}
|
||||
|
||||
offset = *(u32 *)reg;
|
||||
offset = regmap_mmio_get_offset(reg, reg_size);
|
||||
|
||||
while (val_size) {
|
||||
switch (ctx->val_bytes) {
|
||||
|
@ -35,10 +35,14 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
|
||||
unsigned int mask, unsigned int val,
|
||||
bool *change);
|
||||
|
||||
static int _regmap_bus_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *val);
|
||||
static int _regmap_bus_read(void *context, unsigned int reg,
|
||||
unsigned int *val);
|
||||
static int _regmap_bus_formatted_write(void *context, unsigned int reg,
|
||||
unsigned int val);
|
||||
static int _regmap_bus_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val);
|
||||
static int _regmap_bus_raw_write(void *context, unsigned int reg,
|
||||
unsigned int val);
|
||||
|
||||
@ -192,6 +196,13 @@ static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift)
|
||||
b[0] = cpu_to_be16(val << shift);
|
||||
}
|
||||
|
||||
static void regmap_format_16_le(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
__le16 *b = buf;
|
||||
|
||||
b[0] = cpu_to_le16(val << shift);
|
||||
}
|
||||
|
||||
static void regmap_format_16_native(void *buf, unsigned int val,
|
||||
unsigned int shift)
|
||||
{
|
||||
@ -216,6 +227,13 @@ static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift)
|
||||
b[0] = cpu_to_be32(val << shift);
|
||||
}
|
||||
|
||||
static void regmap_format_32_le(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
__le32 *b = buf;
|
||||
|
||||
b[0] = cpu_to_le32(val << shift);
|
||||
}
|
||||
|
||||
static void regmap_format_32_native(void *buf, unsigned int val,
|
||||
unsigned int shift)
|
||||
{
|
||||
@ -240,6 +258,13 @@ static unsigned int regmap_parse_16_be(const void *buf)
|
||||
return be16_to_cpu(b[0]);
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_16_le(const void *buf)
|
||||
{
|
||||
const __le16 *b = buf;
|
||||
|
||||
return le16_to_cpu(b[0]);
|
||||
}
|
||||
|
||||
static void regmap_parse_16_be_inplace(void *buf)
|
||||
{
|
||||
__be16 *b = buf;
|
||||
@ -247,6 +272,13 @@ static void regmap_parse_16_be_inplace(void *buf)
|
||||
b[0] = be16_to_cpu(b[0]);
|
||||
}
|
||||
|
||||
static void regmap_parse_16_le_inplace(void *buf)
|
||||
{
|
||||
__le16 *b = buf;
|
||||
|
||||
b[0] = le16_to_cpu(b[0]);
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_16_native(const void *buf)
|
||||
{
|
||||
return *(u16 *)buf;
|
||||
@ -269,6 +301,13 @@ static unsigned int regmap_parse_32_be(const void *buf)
|
||||
return be32_to_cpu(b[0]);
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_32_le(const void *buf)
|
||||
{
|
||||
const __le32 *b = buf;
|
||||
|
||||
return le32_to_cpu(b[0]);
|
||||
}
|
||||
|
||||
static void regmap_parse_32_be_inplace(void *buf)
|
||||
{
|
||||
__be32 *b = buf;
|
||||
@ -276,6 +315,13 @@ static void regmap_parse_32_be_inplace(void *buf)
|
||||
b[0] = be32_to_cpu(b[0]);
|
||||
}
|
||||
|
||||
static void regmap_parse_32_le_inplace(void *buf)
|
||||
{
|
||||
__le32 *b = buf;
|
||||
|
||||
b[0] = le32_to_cpu(b[0]);
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_32_native(const void *buf)
|
||||
{
|
||||
return *(u32 *)buf;
|
||||
@ -493,6 +539,12 @@ struct regmap *regmap_init(struct device *dev,
|
||||
map->reg_read = config->reg_read;
|
||||
map->reg_write = config->reg_write;
|
||||
|
||||
map->defer_caching = false;
|
||||
goto skip_format_initialization;
|
||||
} else if (!bus->read || !bus->write) {
|
||||
map->reg_read = _regmap_bus_reg_read;
|
||||
map->reg_write = _regmap_bus_reg_write;
|
||||
|
||||
map->defer_caching = false;
|
||||
goto skip_format_initialization;
|
||||
} else {
|
||||
@ -608,6 +660,11 @@ struct regmap *regmap_init(struct device *dev,
|
||||
map->format.parse_val = regmap_parse_16_be;
|
||||
map->format.parse_inplace = regmap_parse_16_be_inplace;
|
||||
break;
|
||||
case REGMAP_ENDIAN_LITTLE:
|
||||
map->format.format_val = regmap_format_16_le;
|
||||
map->format.parse_val = regmap_parse_16_le;
|
||||
map->format.parse_inplace = regmap_parse_16_le_inplace;
|
||||
break;
|
||||
case REGMAP_ENDIAN_NATIVE:
|
||||
map->format.format_val = regmap_format_16_native;
|
||||
map->format.parse_val = regmap_parse_16_native;
|
||||
@ -629,6 +686,11 @@ struct regmap *regmap_init(struct device *dev,
|
||||
map->format.parse_val = regmap_parse_32_be;
|
||||
map->format.parse_inplace = regmap_parse_32_be_inplace;
|
||||
break;
|
||||
case REGMAP_ENDIAN_LITTLE:
|
||||
map->format.format_val = regmap_format_32_le;
|
||||
map->format.parse_val = regmap_parse_32_le;
|
||||
map->format.parse_inplace = regmap_parse_32_le_inplace;
|
||||
break;
|
||||
case REGMAP_ENDIAN_NATIVE:
|
||||
map->format.format_val = regmap_format_32_native;
|
||||
map->format.parse_val = regmap_parse_32_native;
|
||||
@ -1284,6 +1346,14 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _regmap_bus_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct regmap *map = context;
|
||||
|
||||
return map->bus->reg_write(map->bus_context, reg, val);
|
||||
}
|
||||
|
||||
static int _regmap_bus_raw_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
@ -1615,6 +1685,9 @@ static int _regmap_raw_multi_reg_write(struct regmap *map,
|
||||
size_t pair_size = reg_bytes + pad_bytes + val_bytes;
|
||||
size_t len = pair_size * num_regs;
|
||||
|
||||
if (!len)
|
||||
return -EINVAL;
|
||||
|
||||
buf = kzalloc(len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
@ -1662,7 +1735,7 @@ static int _regmap_range_multi_paged_reg_write(struct regmap *map,
|
||||
int ret;
|
||||
int i, n;
|
||||
struct reg_default *base;
|
||||
unsigned int this_page;
|
||||
unsigned int this_page = 0;
|
||||
/*
|
||||
* the set of registers are not neccessarily in order, but
|
||||
* since the order of write must be preserved this algorithm
|
||||
@ -1925,6 +1998,14 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _regmap_bus_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct regmap *map = context;
|
||||
|
||||
return map->bus->reg_read(map->bus_context, reg, val);
|
||||
}
|
||||
|
||||
static int _regmap_bus_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
|
@ -276,6 +276,10 @@ typedef int (*regmap_hw_async_write)(void *context,
|
||||
typedef int (*regmap_hw_read)(void *context,
|
||||
const void *reg_buf, size_t reg_size,
|
||||
void *val_buf, size_t val_size);
|
||||
typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg,
|
||||
unsigned int *val);
|
||||
typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg,
|
||||
unsigned int val);
|
||||
typedef struct regmap_async *(*regmap_hw_async_alloc)(void);
|
||||
typedef void (*regmap_hw_free_context)(void *context);
|
||||
|
||||
@ -309,7 +313,9 @@ struct regmap_bus {
|
||||
regmap_hw_write write;
|
||||
regmap_hw_gather_write gather_write;
|
||||
regmap_hw_async_write async_write;
|
||||
regmap_hw_reg_write reg_write;
|
||||
regmap_hw_read read;
|
||||
regmap_hw_reg_read reg_read;
|
||||
regmap_hw_free_context free_context;
|
||||
regmap_hw_async_alloc async_alloc;
|
||||
u8 read_flag_mask;
|
||||
|
Loading…
x
Reference in New Issue
Block a user