diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 930cad4e5df8a10d7af43c42e2b3d0ebee3bf121..6a7e4fa12854c8ac365edbe6dbf918c04d2ccd59 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -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 { diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index ebd189529760369df33ab021326763b7866c997d..ca193d1ef47cb055f3257a8c8c0b2ba9687a6f5b 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -14,6 +14,79 @@ #include #include + +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); diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index edf88f20cbce8df434058b18f772393e781b4547..6299a50a59607f6d5598eac804817cef47e8c106 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -10,13 +10,13 @@ * published by the Free Software Foundation. */ -#include #include -#include -#include +#include #include +#include #include #include +#include #include #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; diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 1e03e7f8bacb220a47d0edf29700f6a1814fc21e..04a329a377e96b585dd074e2e16340d954e908a4 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -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) { diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 63e30ef096e2be5e16a1e989bba22dd3b6c69f8d..74d8c0672cf6162e9d3d28afbd68f742a5ecb732 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -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) { diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 85691b9b4fa76a778db7f338874849c27d99d74c..7b0e4b425cdf3e66fb5163ae4557adc8c6a89f63 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -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;