diff options
Diffstat (limited to 'drivers/base/regmap/regmap.c')
| -rw-r--r-- | drivers/base/regmap/regmap.c | 1081 |
1 files changed, 742 insertions, 339 deletions
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 4f822e087def..ce9be3989a21 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1,26 +1,23 @@ -/* - * Register map access API - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Register map access API +// +// Copyright 2011 Wolfson Microelectronics plc +// +// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> #include <linux/device.h> #include <linux/slab.h> #include <linux/export.h> #include <linux/mutex.h> #include <linux/err.h> -#include <linux/of.h> +#include <linux/property.h> #include <linux/rbtree.h> #include <linux/sched.h> #include <linux/delay.h> #include <linux/log2.h> #include <linux/hwspinlock.h> +#include <linux/unaligned.h> #define CREATE_TRACE_POINTS #include "trace.h" @@ -92,7 +89,7 @@ EXPORT_SYMBOL_GPL(regmap_check_range_table); bool regmap_writeable(struct regmap *map, unsigned int reg) { - if (map->max_register && reg > map->max_register) + if (map->max_register_is_set && reg > map->max_register) return false; if (map->writeable_reg) @@ -115,7 +112,7 @@ bool regmap_cached(struct regmap *map, unsigned int reg) if (!map->cache_ops) return false; - if (map->max_register && reg > map->max_register) + if (map->max_register_is_set && reg > map->max_register) return false; map->lock(map->lock_arg); @@ -132,7 +129,7 @@ bool regmap_readable(struct regmap *map, unsigned int reg) if (!map->reg_read) return false; - if (map->max_register && reg > map->max_register) + if (map->max_register_is_set && reg > map->max_register) return false; if (map->format.format_write) @@ -212,6 +209,18 @@ static bool regmap_volatile_range(struct regmap *map, unsigned int reg, return true; } +static void regmap_format_12_20_write(struct regmap *map, + unsigned int reg, unsigned int val) +{ + u8 *out = map->work_buf; + + out[0] = reg >> 4; + out[1] = (reg << 4) | (val >> 16); + out[2] = val >> 8; + out[3] = val; +} + + static void regmap_format_2_6_write(struct regmap *map, unsigned int reg, unsigned int val) { @@ -234,6 +243,16 @@ static void regmap_format_7_9_write(struct regmap *map, *out = cpu_to_be16((reg << 9) | val); } +static void regmap_format_7_17_write(struct regmap *map, + unsigned int reg, unsigned int val) +{ + u8 *out = map->work_buf; + + out[2] = val; + out[1] = val >> 8; + out[0] = (val >> 16) | (reg << 1); +} + static void regmap_format_10_14_write(struct regmap *map, unsigned int reg, unsigned int val) { @@ -253,77 +272,45 @@ static void regmap_format_8(void *buf, unsigned int val, unsigned int shift) static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift) { - __be16 *b = buf; - - b[0] = cpu_to_be16(val << shift); + put_unaligned_be16(val << shift, buf); } static void regmap_format_16_le(void *buf, unsigned int val, unsigned int shift) { - __le16 *b = buf; - - b[0] = cpu_to_le16(val << shift); + put_unaligned_le16(val << shift, buf); } static void regmap_format_16_native(void *buf, unsigned int val, unsigned int shift) { - *(u16 *)buf = val << shift; + u16 v = val << shift; + + memcpy(buf, &v, sizeof(v)); } -static void regmap_format_24(void *buf, unsigned int val, unsigned int shift) +static void regmap_format_24_be(void *buf, unsigned int val, unsigned int shift) { - u8 *b = buf; - - val <<= shift; - - b[0] = val >> 16; - b[1] = val >> 8; - b[2] = val; + put_unaligned_be24(val << shift, buf); } static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift) { - __be32 *b = buf; - - b[0] = cpu_to_be32(val << shift); + put_unaligned_be32(val << shift, buf); } static void regmap_format_32_le(void *buf, unsigned int val, unsigned int shift) { - __le32 *b = buf; - - b[0] = cpu_to_le32(val << shift); + put_unaligned_le32(val << shift, buf); } static void regmap_format_32_native(void *buf, unsigned int val, unsigned int shift) { - *(u32 *)buf = val << shift; -} - -#ifdef CONFIG_64BIT -static void regmap_format_64_be(void *buf, unsigned int val, unsigned int shift) -{ - __be64 *b = buf; - - b[0] = cpu_to_be64((u64)val << shift); -} - -static void regmap_format_64_le(void *buf, unsigned int val, unsigned int shift) -{ - __le64 *b = buf; + u32 v = val << shift; - b[0] = cpu_to_le64((u64)val << shift); + memcpy(buf, &v, sizeof(v)); } -static void regmap_format_64_native(void *buf, unsigned int val, - unsigned int shift) -{ - *(u64 *)buf = (u64)val << shift; -} -#endif - static void regmap_parse_inplace_noop(void *buf) { } @@ -337,115 +324,73 @@ static unsigned int regmap_parse_8(const void *buf) static unsigned int regmap_parse_16_be(const void *buf) { - const __be16 *b = buf; - - return be16_to_cpu(b[0]); + return get_unaligned_be16(buf); } static unsigned int regmap_parse_16_le(const void *buf) { - const __le16 *b = buf; - - return le16_to_cpu(b[0]); + return get_unaligned_le16(buf); } static void regmap_parse_16_be_inplace(void *buf) { - __be16 *b = buf; + u16 v = get_unaligned_be16(buf); - b[0] = be16_to_cpu(b[0]); + memcpy(buf, &v, sizeof(v)); } static void regmap_parse_16_le_inplace(void *buf) { - __le16 *b = buf; + u16 v = get_unaligned_le16(buf); - b[0] = le16_to_cpu(b[0]); + memcpy(buf, &v, sizeof(v)); } static unsigned int regmap_parse_16_native(const void *buf) { - return *(u16 *)buf; + u16 v; + + memcpy(&v, buf, sizeof(v)); + return v; } -static unsigned int regmap_parse_24(const void *buf) +static unsigned int regmap_parse_24_be(const void *buf) { - const u8 *b = buf; - unsigned int ret = b[2]; - ret |= ((unsigned int)b[1]) << 8; - ret |= ((unsigned int)b[0]) << 16; - - return ret; + return get_unaligned_be24(buf); } static unsigned int regmap_parse_32_be(const void *buf) { - const __be32 *b = buf; - - return be32_to_cpu(b[0]); + return get_unaligned_be32(buf); } static unsigned int regmap_parse_32_le(const void *buf) { - const __le32 *b = buf; - - return le32_to_cpu(b[0]); + return get_unaligned_le32(buf); } static void regmap_parse_32_be_inplace(void *buf) { - __be32 *b = buf; + u32 v = get_unaligned_be32(buf); - b[0] = be32_to_cpu(b[0]); + memcpy(buf, &v, sizeof(v)); } static void regmap_parse_32_le_inplace(void *buf) { - __le32 *b = buf; + u32 v = get_unaligned_le32(buf); - b[0] = le32_to_cpu(b[0]); + memcpy(buf, &v, sizeof(v)); } static unsigned int regmap_parse_32_native(const void *buf) { - return *(u32 *)buf; -} - -#ifdef CONFIG_64BIT -static unsigned int regmap_parse_64_be(const void *buf) -{ - const __be64 *b = buf; - - return be64_to_cpu(b[0]); -} - -static unsigned int regmap_parse_64_le(const void *buf) -{ - const __le64 *b = buf; - - return le64_to_cpu(b[0]); -} - -static void regmap_parse_64_be_inplace(void *buf) -{ - __be64 *b = buf; + u32 v; - b[0] = be64_to_cpu(b[0]); + memcpy(&v, buf, sizeof(v)); + return v; } -static void regmap_parse_64_le_inplace(void *buf) -{ - __le64 *b = buf; - - b[0] = le64_to_cpu(b[0]); -} - -static unsigned int regmap_parse_64_native(const void *buf) -{ - return *(u64 *)buf; -} -#endif - static void regmap_lock_hwlock(void *__map) { struct regmap *map = __map; @@ -523,6 +468,23 @@ __releases(&map->spinlock) spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags); } +static void regmap_lock_raw_spinlock(void *__map) +__acquires(&map->raw_spinlock) +{ + struct regmap *map = __map; + unsigned long flags; + + raw_spin_lock_irqsave(&map->raw_spinlock, flags); + map->raw_spinlock_flags = flags; +} + +static void regmap_unlock_raw_spinlock(void *__map) +__releases(&map->raw_spinlock) +{ + struct regmap *map = __map; + raw_spin_unlock_irqrestore(&map->raw_spinlock, map->raw_spinlock_flags); +} + static void dev_get_regmap_release(struct device *dev, void *res) { /* @@ -593,14 +555,35 @@ static void regmap_range_exit(struct regmap *map) kfree(map->selector_work_buf); } +static int regmap_set_name(struct regmap *map, const struct regmap_config *config) +{ + if (config->name) { + const char *name = kstrdup_const(config->name, GFP_KERNEL); + + if (!name) + return -ENOMEM; + + kfree_const(map->name); + map->name = name; + } + + return 0; +} + int regmap_attach_dev(struct device *dev, struct regmap *map, const struct regmap_config *config) { struct regmap **m; + int ret; map->dev = dev; - regmap_debugfs_init(map, config->name); + ret = regmap_set_name(map, config); + if (ret) + return ret; + + regmap_debugfs_exit(map); + regmap_debugfs_init(map); /* Add a devres resource for dev_get_regmap() */ m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); @@ -615,6 +598,17 @@ int regmap_attach_dev(struct device *dev, struct regmap *map, } EXPORT_SYMBOL_GPL(regmap_attach_dev); +static int dev_get_regmap_match(struct device *dev, void *res, void *data); + +static int regmap_detach_dev(struct device *dev, struct regmap *map) +{ + if (!dev) + return 0; + + return devres_release(dev, dev_get_regmap_release, + dev_get_regmap_match, (void *)map->name); +} + static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus, const struct regmap_config *config) { @@ -643,7 +637,7 @@ enum regmap_endian regmap_get_val_endian(struct device *dev, const struct regmap_bus *bus, const struct regmap_config *config) { - struct device_node *np; + struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL; enum regmap_endian endian; /* Retrieve the endianness specification from the regmap config */ @@ -653,22 +647,17 @@ enum regmap_endian regmap_get_val_endian(struct device *dev, if (endian != REGMAP_ENDIAN_DEFAULT) return endian; - /* If the dev and dev->of_node exist try to get endianness from DT */ - if (dev && dev->of_node) { - np = dev->of_node; - - /* Parse the device's DT node for an endianness specification */ - if (of_property_read_bool(np, "big-endian")) - endian = REGMAP_ENDIAN_BIG; - else if (of_property_read_bool(np, "little-endian")) - endian = REGMAP_ENDIAN_LITTLE; - else if (of_property_read_bool(np, "native-endian")) - endian = REGMAP_ENDIAN_NATIVE; - - /* If the endianness was specified in DT, use that */ - if (endian != REGMAP_ENDIAN_DEFAULT) - return endian; - } + /* If the firmware node exist try to get endianness from it */ + if (fwnode_property_read_bool(fwnode, "big-endian")) + endian = REGMAP_ENDIAN_BIG; + else if (fwnode_property_read_bool(fwnode, "little-endian")) + endian = REGMAP_ENDIAN_LITTLE; + else if (fwnode_property_read_bool(fwnode, "native-endian")) + endian = REGMAP_ENDIAN_NATIVE; + + /* If the endianness was specified in fwnode, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; /* Retrieve the endianness specification from the bus config */ if (bus && bus->val_format_endian_default) @@ -704,21 +693,21 @@ struct regmap *__regmap_init(struct device *dev, goto err; } - if (config->name) { - map->name = kstrdup_const(config->name, GFP_KERNEL); - if (!map->name) { - ret = -ENOMEM; - goto err_map; - } - } + ret = regmap_set_name(map, config); + if (ret) + goto err_map; + + ret = -EINVAL; /* Later error paths rely on this */ if (config->disable_locking) { map->lock = map->unlock = regmap_lock_unlock_none; + map->can_sleep = config->can_sleep; regmap_debugfs_disable(map); } else if (config->lock && config->unlock) { map->lock = config->lock; map->unlock = config->unlock; map->lock_arg = config->lock_arg; + map->can_sleep = config->can_sleep; } else if (config->use_hwlock) { map->hwlock = hwspin_lock_request_specific(config->hwlock_id); if (!map->hwlock) { @@ -745,19 +734,29 @@ struct regmap *__regmap_init(struct device *dev, } else { if ((bus && bus->fast_io) || config->fast_io) { - spin_lock_init(&map->spinlock); - map->lock = regmap_lock_spinlock; - map->unlock = regmap_unlock_spinlock; - lockdep_set_class_and_name(&map->spinlock, - lock_key, lock_name); + if (config->use_raw_spinlock) { + raw_spin_lock_init(&map->raw_spinlock); + map->lock = regmap_lock_raw_spinlock; + map->unlock = regmap_unlock_raw_spinlock; + lockdep_set_class_and_name(&map->raw_spinlock, + lock_key, lock_name); + } else { + spin_lock_init(&map->spinlock); + map->lock = regmap_lock_spinlock; + map->unlock = regmap_unlock_spinlock; + lockdep_set_class_and_name(&map->spinlock, + lock_key, lock_name); + } } else { mutex_init(&map->mutex); map->lock = regmap_lock_mutex; map->unlock = regmap_unlock_mutex; + map->can_sleep = true; lockdep_set_class_and_name(&map->mutex, lock_key, lock_name); } map->lock_arg = map; + map->lock_key = lock_key; } /* @@ -769,12 +768,14 @@ struct regmap *__regmap_init(struct device *dev, else map->alloc_flags = GFP_KERNEL; - map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); - map->format.pad_bytes = config->pad_bits / 8; - map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); - map->format.buf_size = DIV_ROUND_UP(config->reg_bits + - config->val_bits + config->pad_bits, 8); + map->reg_base = config->reg_base; map->reg_shift = config->pad_bits % 8; + + map->format.pad_bytes = config->pad_bits / 8; + map->format.reg_shift = config->reg_shift; + map->format.reg_bytes = BITS_TO_BYTES(config->reg_bits); + map->format.val_bytes = BITS_TO_BYTES(config->val_bits); + map->format.buf_size = BITS_TO_BYTES(config->reg_bits + config->val_bits + config->pad_bits); if (config->reg_stride) map->reg_stride = config->reg_stride; else @@ -783,17 +784,21 @@ struct regmap *__regmap_init(struct device *dev, map->reg_stride_order = ilog2(map->reg_stride); else map->reg_stride_order = -1; - map->use_single_read = config->use_single_read || !bus || !bus->read; - map->use_single_write = config->use_single_write || !bus || !bus->write; - map->can_multi_write = config->can_multi_write && bus && bus->write; + map->use_single_read = config->use_single_read || !(config->read || (bus && bus->read)); + map->use_single_write = config->use_single_write || !(config->write || (bus && bus->write)); + map->can_multi_write = config->can_multi_write && (config->write || (bus && bus->write)); if (bus) { map->max_raw_read = bus->max_raw_read; map->max_raw_write = bus->max_raw_write; + } else if (config->max_raw_read && config->max_raw_write) { + map->max_raw_read = config->max_raw_read; + map->max_raw_write = config->max_raw_write; } map->dev = dev; map->bus = bus; map->bus_context = bus_context; map->max_register = config->max_register; + map->max_register_is_set = map->max_register ?: config->max_register_is_0; map->wr_table = config->wr_table; map->rd_table = config->rd_table; map->volatile_table = config->volatile_table; @@ -822,25 +827,41 @@ struct regmap *__regmap_init(struct device *dev, map->read_flag_mask = bus->read_flag_mask; } - if (!bus) { + if (config->read && config->write) { + map->reg_read = _regmap_bus_read; + if (config->reg_update_bits) + map->reg_update_bits = config->reg_update_bits; + + /* Bulk read/write */ + map->read = config->read; + map->write = config->write; + + reg_endian = REGMAP_ENDIAN_NATIVE; + val_endian = REGMAP_ENDIAN_NATIVE; + } else if (!bus) { map->reg_read = config->reg_read; map->reg_write = config->reg_write; + map->reg_update_bits = config->reg_update_bits; 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->reg_update_bits = bus->reg_update_bits; map->defer_caching = false; goto skip_format_initialization; } else { map->reg_read = _regmap_bus_read; map->reg_update_bits = bus->reg_update_bits; - } + /* Bulk read/write */ + map->read = bus->read; + map->write = bus->write; - reg_endian = regmap_get_reg_endian(bus, config); - val_endian = regmap_get_val_endian(dev, bus, config); + reg_endian = regmap_get_reg_endian(bus, config); + val_endian = regmap_get_val_endian(dev, bus, config); + } switch (config->reg_bits + map->reg_shift) { case 2: @@ -868,6 +889,9 @@ struct regmap *__regmap_init(struct device *dev, case 9: map->format.format_write = regmap_format_7_9_write; break; + case 17: + map->format.format_write = regmap_format_7_17_write; + break; default: goto err_hwlock; } @@ -883,6 +907,16 @@ struct regmap *__regmap_init(struct device *dev, } break; + case 12: + switch (config->val_bits) { + case 20: + map->format.format_write = regmap_format_12_20_write; + break; + default: + goto err_hwlock; + } + break; + case 8: map->format.format_reg = regmap_format_8; break; @@ -904,44 +938,30 @@ struct regmap *__regmap_init(struct device *dev, break; case 24: - if (reg_endian != REGMAP_ENDIAN_BIG) - goto err_hwlock; - map->format.format_reg = regmap_format_24; - break; - - case 32: switch (reg_endian) { case REGMAP_ENDIAN_BIG: - map->format.format_reg = regmap_format_32_be; - break; - case REGMAP_ENDIAN_LITTLE: - map->format.format_reg = regmap_format_32_le; - break; - case REGMAP_ENDIAN_NATIVE: - map->format.format_reg = regmap_format_32_native; + map->format.format_reg = regmap_format_24_be; break; default: goto err_hwlock; } break; -#ifdef CONFIG_64BIT - case 64: + case 32: switch (reg_endian) { case REGMAP_ENDIAN_BIG: - map->format.format_reg = regmap_format_64_be; + map->format.format_reg = regmap_format_32_be; break; case REGMAP_ENDIAN_LITTLE: - map->format.format_reg = regmap_format_64_le; + map->format.format_reg = regmap_format_32_le; break; case REGMAP_ENDIAN_NATIVE: - map->format.format_reg = regmap_format_64_native; + map->format.format_reg = regmap_format_32_native; break; default: goto err_hwlock; } break; -#endif default: goto err_hwlock; @@ -977,10 +997,14 @@ struct regmap *__regmap_init(struct device *dev, } break; case 24: - if (val_endian != REGMAP_ENDIAN_BIG) + switch (val_endian) { + case REGMAP_ENDIAN_BIG: + map->format.format_val = regmap_format_24_be; + map->format.parse_val = regmap_parse_24_be; + break; + default: goto err_hwlock; - map->format.format_val = regmap_format_24; - map->format.parse_val = regmap_parse_24; + } break; case 32: switch (val_endian) { @@ -1002,28 +1026,6 @@ struct regmap *__regmap_init(struct device *dev, goto err_hwlock; } break; -#ifdef CONFIG_64BIT - case 64: - switch (val_endian) { - case REGMAP_ENDIAN_BIG: - map->format.format_val = regmap_format_64_be; - map->format.parse_val = regmap_parse_64_be; - map->format.parse_inplace = regmap_parse_64_be_inplace; - break; - case REGMAP_ENDIAN_LITTLE: - map->format.format_val = regmap_format_64_le; - map->format.parse_val = regmap_parse_64_le; - map->format.parse_inplace = regmap_parse_64_le_inplace; - break; - case REGMAP_ENDIAN_NATIVE: - map->format.format_val = regmap_format_64_native; - map->format.parse_val = regmap_parse_64_native; - break; - default: - goto err_hwlock; - } - break; -#endif } if (map->format.format_write) { @@ -1060,13 +1062,13 @@ skip_format_initialization: /* Sanity check */ if (range_cfg->range_max < range_cfg->range_min) { - dev_err(map->dev, "Invalid range %d: %d < %d\n", i, + dev_err(map->dev, "Invalid range %d: %u < %u\n", i, range_cfg->range_max, range_cfg->range_min); goto err_range; } if (range_cfg->range_max > map->max_register) { - dev_err(map->dev, "Invalid range %d: %d > %d\n", i, + dev_err(map->dev, "Invalid range %d: %u > %u\n", i, range_cfg->range_max, map->max_register); goto err_range; } @@ -1086,10 +1088,10 @@ skip_format_initialization: /* Make sure, that this register range has no selector or data window within its boundary */ for (j = 0; j < config->num_ranges; j++) { - unsigned sel_reg = config->ranges[j].selector_reg; - unsigned win_min = config->ranges[j].window_start; - unsigned win_max = win_min + - config->ranges[j].window_len - 1; + unsigned int sel_reg = config->ranges[j].selector_reg; + unsigned int win_min = config->ranges[j].window_start; + unsigned int win_max = win_min + + config->ranges[j].window_len - 1; /* Allow data window inside its own virtual range */ if (j == i) @@ -1153,7 +1155,7 @@ skip_format_initialization: if (ret != 0) goto err_regcache; } else { - regmap_debugfs_init(map, config->name); + regmap_debugfs_init(map); } return map; @@ -1171,6 +1173,8 @@ err_name: err_map: kfree(map); err: + if (bus && bus->free_on_exit) + kfree(bus); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(__regmap_init); @@ -1213,6 +1217,9 @@ static void regmap_field_init(struct regmap_field *rm_field, rm_field->reg = reg_field.reg; rm_field->shift = reg_field.lsb; rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb); + + WARN_ONCE(rm_field->mask == 0, "invalid empty mask defined\n"); + rm_field->id_size = reg_field.id_size; rm_field->id_offset = reg_field.id_offset; } @@ -1243,6 +1250,106 @@ struct regmap_field *devm_regmap_field_alloc(struct device *dev, } EXPORT_SYMBOL_GPL(devm_regmap_field_alloc); + +/** + * regmap_field_bulk_alloc() - Allocate and initialise a bulk register field. + * + * @regmap: regmap bank in which this register field is located. + * @rm_field: regmap register fields within the bank. + * @reg_field: Register fields within the bank. + * @num_fields: Number of register fields. + * + * The return value will be an -ENOMEM on error or zero for success. + * Newly allocated regmap_fields should be freed by calling + * regmap_field_bulk_free() + */ +int regmap_field_bulk_alloc(struct regmap *regmap, + struct regmap_field **rm_field, + const struct reg_field *reg_field, + int num_fields) +{ + struct regmap_field *rf; + int i; + + rf = kcalloc(num_fields, sizeof(*rf), GFP_KERNEL); + if (!rf) + return -ENOMEM; + + for (i = 0; i < num_fields; i++) { + regmap_field_init(&rf[i], regmap, reg_field[i]); + rm_field[i] = &rf[i]; + } + + return 0; +} +EXPORT_SYMBOL_GPL(regmap_field_bulk_alloc); + +/** + * devm_regmap_field_bulk_alloc() - Allocate and initialise a bulk register + * fields. + * + * @dev: Device that will be interacted with + * @regmap: regmap bank in which this register field is located. + * @rm_field: regmap register fields within the bank. + * @reg_field: Register fields within the bank. + * @num_fields: Number of register fields. + * + * The return value will be an -ENOMEM on error or zero for success. + * Newly allocated regmap_fields will be automatically freed by the + * device management code. + */ +int devm_regmap_field_bulk_alloc(struct device *dev, + struct regmap *regmap, + struct regmap_field **rm_field, + const struct reg_field *reg_field, + int num_fields) +{ + struct regmap_field *rf; + int i; + + rf = devm_kcalloc(dev, num_fields, sizeof(*rf), GFP_KERNEL); + if (!rf) + return -ENOMEM; + + for (i = 0; i < num_fields; i++) { + regmap_field_init(&rf[i], regmap, reg_field[i]); + rm_field[i] = &rf[i]; + } + + return 0; +} +EXPORT_SYMBOL_GPL(devm_regmap_field_bulk_alloc); + +/** + * regmap_field_bulk_free() - Free register field allocated using + * regmap_field_bulk_alloc. + * + * @field: regmap fields which should be freed. + */ +void regmap_field_bulk_free(struct regmap_field *field) +{ + kfree(field); +} +EXPORT_SYMBOL_GPL(regmap_field_bulk_free); + +/** + * devm_regmap_field_bulk_free() - Free a bulk register field allocated using + * devm_regmap_field_bulk_alloc. + * + * @dev: Device that will be interacted with + * @field: regmap field which should be freed. + * + * Free register field allocated using devm_regmap_field_bulk_alloc(). Usually + * drivers need not call this function, as the memory allocated via devm + * will be freed as per device-driver life-cycle. + */ +void devm_regmap_field_bulk_free(struct device *dev, + struct regmap_field *field) +{ + devm_kfree(dev, field); +} +EXPORT_SYMBOL_GPL(devm_regmap_field_bulk_free); + /** * devm_regmap_field_free() - Free a register field allocated using * devm_regmap_field_alloc. @@ -1313,10 +1420,13 @@ EXPORT_SYMBOL_GPL(regmap_field_free); */ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) { + int ret; + regcache_exit(map); regmap_debugfs_exit(map); map->max_register = config->max_register; + map->max_register_is_set = map->max_register ?: config->max_register_is_0; map->writeable_reg = config->writeable_reg; map->readable_reg = config->readable_reg; map->volatile_reg = config->volatile_reg; @@ -1325,7 +1435,11 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) map->readable_noinc_reg = config->readable_noinc_reg; map->cache_type = config->cache_type; - regmap_debugfs_init(map, config->name); + ret = regmap_set_name(map, config); + if (ret) + return ret; + + regmap_debugfs_init(map); map->cache_bypass = false; map->cache_only = false; @@ -1343,7 +1457,9 @@ void regmap_exit(struct regmap *map) { struct regmap_async *async; + regmap_detach_dev(map->dev, map); regcache_exit(map); + regmap_debugfs_exit(map); regmap_range_exit(map); if (map->bus && map->bus->free_context) @@ -1359,7 +1475,12 @@ void regmap_exit(struct regmap *map) } if (map->hwlock) hwspin_lock_free(map->hwlock); + if (map->lock == regmap_lock_mutex) + mutex_destroy(&map->mutex); kfree_const(map->name); + kfree(map->patch); + if (map->bus && map->bus->free_on_exit) + kfree(map->bus); kfree(map); } EXPORT_SYMBOL_GPL(regmap_exit); @@ -1374,7 +1495,7 @@ static int dev_get_regmap_match(struct device *dev, void *res, void *data) /* If the user didn't specify a name match any */ if (data) - return (*r)->name == data; + return (*r)->name && !strcmp((*r)->name, data); else return 1; } @@ -1478,8 +1599,20 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes, buf[i] |= (mask >> (8 * i)) & 0xff; } +static unsigned int regmap_reg_addr(struct regmap *map, unsigned int reg) +{ + reg += map->reg_base; + + if (map->format.reg_shift > 0) + reg >>= map->format.reg_shift; + else if (map->format.reg_shift < 0) + reg <<= -(map->format.reg_shift); + + return reg; +} + static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, - const void *val, size_t val_len) + const void *val, size_t val_len, bool noinc) { struct regmap_range_node *range; unsigned long flags; @@ -1490,27 +1623,33 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, size_t len; int i; - WARN_ON(!map->bus); - - /* 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 + regmap_get_offset(map, i))) + /* Check for unwritable or noinc registers in range + * before we start + */ + if (!regmap_writeable_noinc(map, reg)) { + for (i = 0; i < val_len / map->format.val_bytes; i++) { + unsigned int element = + reg + regmap_get_offset(map, i); + if (!regmap_writeable(map, element) || + regmap_writeable_noinc(map, element)) return -EINVAL; + } + } if (!map->cache_bypass && map->format.parse_val) { - unsigned int ival; + unsigned int ival, offset; int val_bytes = map->format.val_bytes; - for (i = 0; i < val_len / val_bytes; i++) { - ival = map->format.parse_val(val + (i * val_bytes)); - ret = regcache_write(map, - reg + regmap_get_offset(map, i), - ival); + + /* Cache the last written value for noinc writes */ + i = noinc ? val_len - val_bytes : 0; + for (; i < val_len; i += val_bytes) { + ival = map->format.parse_val(val + i); + offset = noinc ? 0 : regmap_get_offset(map, i / val_bytes); + ret = regcache_write(map, reg + offset, ival); if (ret) { dev_err(map->dev, "Error in caching of register: %x ret: %d\n", - reg + i, ret); + reg + offset, ret); return ret; } } @@ -1532,7 +1671,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, win_residue, val_len / map->format.val_bytes); ret = _regmap_raw_write_impl(map, reg, val, win_residue * - map->format.val_bytes); + map->format.val_bytes, noinc); if (ret != 0) return ret; @@ -1546,11 +1685,12 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, win_residue = range->window_len - win_offset; } - ret = _regmap_select_page(map, ®, range, val_num); + ret = _regmap_select_page(map, ®, range, noinc ? 1 : val_num); if (ret != 0) return ret; } + reg = regmap_reg_addr(map, reg); map->format.format_reg(map->work_buf, reg, map->reg_shift); regmap_set_work_buf_flag_mask(map, map->format.reg_bytes, map->write_flag_mask); @@ -1565,7 +1705,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, val = work_val; } - if (map->async && map->bus->async_write) { + if (map->async && map->bus && map->bus->async_write) { struct regmap_async *async; trace_regmap_async_write_start(map, reg, val_len); @@ -1633,15 +1773,17 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, * write. */ if (val == work_val) - ret = map->bus->write(map->bus_context, map->work_buf, - map->format.reg_bytes + - map->format.pad_bytes + - val_len); - else if (map->bus->gather_write) + ret = map->write(map->bus_context, map->work_buf, + map->format.reg_bytes + + map->format.pad_bytes + + val_len); + else if (map->bus && map->bus->gather_write) ret = map->bus->gather_write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); + else + ret = -ENOTSUPP; /* If that didn't work fall back on linearising by hand. */ if (ret == -ENOTSUPP) { @@ -1653,7 +1795,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, memcpy(buf, map->work_buf, map->format.reg_bytes); memcpy(buf + map->format.reg_bytes + map->format.pad_bytes, val, val_len); - ret = map->bus->write(map->bus_context, buf, len); + ret = map->write(map->bus_context, buf, len); kfree(buf); } else if (ret != 0 && !map->cache_bypass && map->format.parse_val) { @@ -1676,8 +1818,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, */ bool regmap_can_raw_write(struct regmap *map) { - return map->bus && map->bus->write && map->format.format_val && - map->format.format_reg; + return map->write && map->format.format_val && map->format.format_reg; } EXPORT_SYMBOL_GPL(regmap_can_raw_write); @@ -1710,7 +1851,7 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, struct regmap_range_node *range; struct regmap *map = context; - WARN_ON(!map->bus || !map->format.format_write); + WARN_ON(!map->format.format_write); range = _regmap_range_lookup(map, reg); if (range) { @@ -1719,12 +1860,12 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, return ret; } + reg = regmap_reg_addr(map, reg); map->format.format_write(map, reg, val); trace_regmap_hw_write_start(map, reg, 1); - ret = map->bus->write(map->bus_context, map->work_buf, - map->format.buf_size); + ret = map->write(map->bus_context, map->work_buf, map->format.buf_size); trace_regmap_hw_write_done(map, reg, 1); @@ -1735,7 +1876,17 @@ static int _regmap_bus_reg_write(void *context, unsigned int reg, unsigned int val) { struct regmap *map = context; + struct regmap_range_node *range; + int ret; + range = _regmap_range_lookup(map, reg); + if (range) { + ret = _regmap_select_page(map, ®, range, 1); + if (ret != 0) + return ret; + } + + reg = regmap_reg_addr(map, reg); return map->bus->reg_write(map->bus_context, reg, val); } @@ -1744,7 +1895,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, { struct regmap *map = context; - WARN_ON(!map->bus || !map->format.format_val); + WARN_ON(!map->format.format_val); map->format.format_val(map->work_buf + map->format.reg_bytes + map->format.pad_bytes, val, 0); @@ -1752,12 +1903,13 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, map->work_buf + map->format.reg_bytes + map->format.pad_bytes, - map->format.val_bytes); + map->format.val_bytes, + false); } static inline void *_regmap_map_get_context(struct regmap *map) { - return (map->bus) ? map : map->bus_context; + return (map->bus || (!map->bus && map->read)) ? map : map->bus_context; } int _regmap_write(struct regmap *map, unsigned int reg, @@ -1779,12 +1931,15 @@ int _regmap_write(struct regmap *map, unsigned int reg, } } - if (regmap_should_log(map)) - dev_info(map->dev, "%x <= %x\n", reg, val); + ret = map->reg_write(context, reg, val); + if (ret == 0) { + if (regmap_should_log(map)) + dev_info(map->dev, "%x <= %x\n", reg, val); - trace_regmap_reg_write(map, reg, val); + trace_regmap_reg_write(map, reg, val); + } - return map->reg_write(context, reg, val); + return ret; } /** @@ -1846,7 +2001,7 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val) EXPORT_SYMBOL_GPL(regmap_write_async); int _regmap_raw_write(struct regmap *map, unsigned int reg, - const void *val, size_t val_len) + const void *val, size_t val_len, bool noinc) { size_t val_bytes = map->format.val_bytes; size_t val_count = val_len / val_bytes; @@ -1867,7 +2022,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, /* Write as many bytes as possible with chunk_size */ for (i = 0; i < chunk_count; i++) { - ret = _regmap_raw_write_impl(map, reg, val, chunk_bytes); + ret = _regmap_raw_write_impl(map, reg, val, chunk_bytes, noinc); if (ret) return ret; @@ -1878,7 +2033,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, /* Write remaining bytes */ if (val_len) - ret = _regmap_raw_write_impl(map, reg, val, val_len); + ret = _regmap_raw_write_impl(map, reg, val, val_len, noinc); return ret; } @@ -1911,7 +2066,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, map->lock(map->lock_arg); - ret = _regmap_raw_write(map, reg, val, val_len); + ret = _regmap_raw_write(map, reg, val, val_len, false); map->unlock(map->lock_arg); @@ -1919,8 +2074,86 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_raw_write); +static int regmap_noinc_readwrite(struct regmap *map, unsigned int reg, + void *val, unsigned int val_len, bool write) +{ + size_t val_bytes = map->format.val_bytes; + size_t val_count = val_len / val_bytes; + unsigned int lastval; + u8 *u8p; + u16 *u16p; + u32 *u32p; + int ret; + int i; + + switch (val_bytes) { + case 1: + u8p = val; + if (write) + lastval = (unsigned int)u8p[val_count - 1]; + break; + case 2: + u16p = val; + if (write) + lastval = (unsigned int)u16p[val_count - 1]; + break; + case 4: + u32p = val; + if (write) + lastval = (unsigned int)u32p[val_count - 1]; + break; + default: + return -EINVAL; + } + + /* + * Update the cache with the last value we write, the rest is just + * gone down in the hardware FIFO. We can't cache FIFOs. This makes + * sure a single read from the cache will work. + */ + if (write) { + if (!map->cache_bypass && !map->defer_caching) { + ret = regcache_write(map, reg, lastval); + if (ret != 0) + return ret; + if (map->cache_only) { + map->cache_dirty = true; + return 0; + } + } + ret = map->bus->reg_noinc_write(map->bus_context, reg, val, val_count); + } else { + ret = map->bus->reg_noinc_read(map->bus_context, reg, val, val_count); + } + + if (!ret && regmap_should_log(map)) { + dev_info(map->dev, "%x %s [", reg, write ? "<=" : "=>"); + for (i = 0; i < val_count; i++) { + switch (val_bytes) { + case 1: + pr_cont("%x", u8p[i]); + break; + case 2: + pr_cont("%x", u16p[i]); + break; + case 4: + pr_cont("%x", u32p[i]); + break; + default: + break; + } + if (i == (val_count - 1)) + pr_cont("]\n"); + else + pr_cont(","); + } + } + + return 0; +} + /** - * regmap_noinc_write(): Write data from a register without incrementing the + * regmap_noinc_write(): Write data to a register without incrementing the * register number * * @map: Register map to write to @@ -1946,10 +2179,8 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg, size_t write_len; int ret; - if (!map->bus) + if (!map->write && !(map->bus && map->bus->reg_noinc_write)) return -EINVAL; - if (!map->bus->write) - return -ENOTSUPP; if (val_len % map->format.val_bytes) return -EINVAL; if (!IS_ALIGNED(reg, map->reg_stride)) @@ -1964,12 +2195,21 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg, goto out_unlock; } + /* + * Use the accelerated operation if we can. The val drops the const + * typing in order to facilitate code reuse in regmap_noinc_readwrite(). + */ + if (map->bus->reg_noinc_write) { + ret = regmap_noinc_readwrite(map, reg, (void *)val, val_len, true); + goto out_unlock; + } + while (val_len) { if (map->max_raw_write && map->max_raw_write < val_len) write_len = map->max_raw_write; else write_len = val_len; - ret = _regmap_raw_write(map, reg, val, write_len); + ret = _regmap_raw_write(map, reg, val, write_len, true); if (ret) goto out_unlock; val = ((u8 *)val) + write_len; @@ -2012,6 +2252,30 @@ int regmap_field_update_bits_base(struct regmap_field *field, EXPORT_SYMBOL_GPL(regmap_field_update_bits_base); /** + * regmap_field_test_bits() - Check if all specified bits are set in a + * register field. + * + * @field: Register field to operate on + * @bits: Bits to test + * + * Returns negative errno if the underlying regmap_field_read() fails, + * 0 if at least one of the tested bits is not set and 1 if all tested + * bits are set. + */ +int regmap_field_test_bits(struct regmap_field *field, unsigned int bits) +{ + unsigned int val; + int ret; + + ret = regmap_field_read(field, &val); + if (ret) + return ret; + + return (val & bits) == bits; +} +EXPORT_SYMBOL_GPL(regmap_field_test_bits); + +/** * regmap_fields_update_bits_base() - Perform a read/modify/write cycle a * register field with port ID * @@ -2026,7 +2290,7 @@ EXPORT_SYMBOL_GPL(regmap_field_update_bits_base); * A value of zero will be returned on success, a negative errno will * be returned in error cases. */ -int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id, +int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id, unsigned int mask, unsigned int val, bool *change, bool async, bool force) { @@ -2069,7 +2333,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, * Some devices don't support bulk write, for them we have a series of * single write operations. */ - if (!map->bus || !map->format.parse_inplace) { + if (!map->write || !map->format.parse_inplace) { map->lock(map->lock_arg); for (i = 0; i < val_count; i++) { unsigned int ival; @@ -2084,11 +2348,6 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, case 4: ival = *(u32 *)(val + (i * val_bytes)); break; -#ifdef CONFIG_64BIT - case 8: - ival = *(u64 *)(val + (i * val_bytes)); - break; -#endif default: ret = -EINVAL; goto out; @@ -2105,7 +2364,7 @@ out: } else { void *wval; - wval = kmemdup(val, val_count * val_bytes, map->alloc_flags); + wval = kmemdup_array(val, val_count, val_bytes, map->alloc_flags); if (!wval) return -ENOMEM; @@ -2116,6 +2375,10 @@ out: kfree(wval); } + + if (!ret) + trace_regmap_bulk_write(map, reg, val, val_bytes * val_count); + return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_write); @@ -2156,6 +2419,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map, unsigned int reg = regs[i].reg; unsigned int val = regs[i].def; trace_regmap_hw_write_start(map, reg, 1); + reg = regmap_reg_addr(map, reg); map->format.format_reg(u8, reg, map->reg_shift); u8 += reg_bytes + pad_bytes; map->format.format_val(u8, val, 0); @@ -2164,7 +2428,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map, u8 = buf; *u8 |= map->write_flag_mask; - ret = map->bus->write(map->bus_context, buf, len); + ret = map->write(map->bus_context, buf, len); kfree(buf); @@ -2237,8 +2501,12 @@ static int _regmap_range_multi_paged_reg_write(struct regmap *map, if (ret != 0) return ret; - if (regs[i].delay_us) - udelay(regs[i].delay_us); + if (regs[i].delay_us) { + if (map->can_sleep) + fsleep(regs[i].delay_us); + else + udelay(regs[i].delay_us); + } base += n; n = 0; @@ -2274,8 +2542,12 @@ static int _regmap_multi_reg_write(struct regmap *map, if (ret != 0) return ret; - if (regs[i].delay_us) - udelay(regs[i].delay_us); + if (regs[i].delay_us) { + if (map->can_sleep) + fsleep(regs[i].delay_us); + else + udelay(regs[i].delay_us); + } } return 0; } @@ -2446,7 +2718,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg, map->async = true; - ret = _regmap_raw_write(map, reg, val, val_len); + ret = _regmap_raw_write(map, reg, val, val_len, false); map->async = false; @@ -2457,32 +2729,31 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg, EXPORT_SYMBOL_GPL(regmap_raw_write_async); static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, - unsigned int val_len) + unsigned int val_len, bool noinc) { struct regmap_range_node *range; int ret; - WARN_ON(!map->bus); - - if (!map->bus || !map->bus->read) + if (!map->read) return -EINVAL; range = _regmap_range_lookup(map, reg); if (range) { ret = _regmap_select_page(map, ®, range, - val_len / map->format.val_bytes); + noinc ? 1 : val_len / map->format.val_bytes); if (ret != 0) return ret; } + reg = regmap_reg_addr(map, reg); map->format.format_reg(map->work_buf, reg, map->reg_shift); regmap_set_work_buf_flag_mask(map, map->format.reg_bytes, map->read_flag_mask); trace_regmap_hw_read_start(map, reg, val_len / map->format.val_bytes); - ret = map->bus->read(map->bus_context, map->work_buf, - map->format.reg_bytes + map->format.pad_bytes, - val, val_len); + ret = map->read(map->bus_context, map->work_buf, + map->format.reg_bytes + map->format.pad_bytes, + val, val_len); trace_regmap_hw_read_done(map, reg, val_len / map->format.val_bytes); @@ -2493,7 +2764,17 @@ static int _regmap_bus_reg_read(void *context, unsigned int reg, unsigned int *val) { struct regmap *map = context; + struct regmap_range_node *range; + int ret; + range = _regmap_range_lookup(map, reg); + if (range) { + ret = _regmap_select_page(map, ®, range, 1); + if (ret != 0) + return ret; + } + + reg = regmap_reg_addr(map, reg); return map->bus->reg_read(map->bus_context, reg, val); } @@ -2508,7 +2789,7 @@ static int _regmap_bus_read(void *context, unsigned int reg, if (!map->format.parse_val) return -EINVAL; - ret = _regmap_raw_read(map, reg, work_val, map->format.val_bytes); + ret = _regmap_raw_read(map, reg, work_val, map->format.val_bytes, false); if (ret == 0) *val = map->format.parse_val(work_val); @@ -2575,6 +2856,43 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) EXPORT_SYMBOL_GPL(regmap_read); /** + * regmap_read_bypassed() - Read a value from a single register direct + * from the device, bypassing the cache + * + * @map: Register map to read from + * @reg: Register to be read from + * @val: Pointer to store read value + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val) +{ + int ret; + bool bypass, cache_only; + + if (!IS_ALIGNED(reg, map->reg_stride)) + return -EINVAL; + + map->lock(map->lock_arg); + + bypass = map->cache_bypass; + cache_only = map->cache_only; + map->cache_bypass = true; + map->cache_only = false; + + ret = _regmap_read(map, reg, val); + + map->cache_bypass = bypass; + map->cache_only = cache_only; + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_read_bypassed); + +/** * regmap_raw_read() - Read raw data from the device * * @map: Register map to read from @@ -2593,8 +2911,6 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int v; int ret, i; - if (!map->bus) - return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; if (!IS_ALIGNED(reg, map->reg_stride)) @@ -2609,7 +2925,12 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t chunk_count, chunk_bytes; size_t chunk_regs = val_count; - if (!map->bus->read) { + if (!map->cache_bypass && map->cache_only) { + ret = -EBUSY; + goto out; + } + + if (!map->read) { ret = -ENOTSUPP; goto out; } @@ -2624,7 +2945,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, /* Read bytes that fit into whole chunks */ for (i = 0; i < chunk_count; i++) { - ret = _regmap_raw_read(map, reg, val, chunk_bytes); + ret = _regmap_raw_read(map, reg, val, chunk_bytes, false); if (ret != 0) goto out; @@ -2635,7 +2956,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, /* Read remaining bytes */ if (val_len) { - ret = _regmap_raw_read(map, reg, val, val_len); + ret = _regmap_raw_read(map, reg, val, val_len, false); if (ret != 0) goto out; } @@ -2669,7 +2990,7 @@ EXPORT_SYMBOL_GPL(regmap_raw_read); * @val: Pointer to data buffer * @val_len: Length of output buffer in bytes. * - * The regmap API usually assumes that bulk bus read operations will read a + * The regmap API usually assumes that bulk read operations will read a * range of registers. Some devices have certain registers for which a read * operation read will read from an internal FIFO. * @@ -2687,10 +3008,9 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg, size_t read_len; int ret; - if (!map->bus) - return -EINVAL; - if (!map->bus->read) + if (!map->read) return -ENOTSUPP; + if (val_len % map->format.val_bytes) return -EINVAL; if (!IS_ALIGNED(reg, map->reg_stride)) @@ -2705,12 +3025,29 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg, goto out_unlock; } + /* + * We have not defined the FIFO semantics for cache, as the + * cache is just one value deep. Should we return the last + * written value? Just avoid this by always reading the FIFO + * even when using cache. Cache only will not work. + */ + if (!map->cache_bypass && map->cache_only) { + ret = -EBUSY; + goto out_unlock; + } + + /* Use the accelerated operation if we can */ + if (map->bus->reg_noinc_read) { + ret = regmap_noinc_readwrite(map, reg, val, val_len, false); + goto out_unlock; + } + while (val_len) { if (map->max_raw_read && map->max_raw_read < val_len) read_len = map->max_raw_read; else read_len = val_len; - ret = _regmap_raw_read(map, reg, val, read_len); + ret = _regmap_raw_read(map, reg, val, read_len, true); if (ret) goto out_unlock; val = ((u8 *)val) + read_len; @@ -2781,8 +3118,53 @@ int regmap_fields_read(struct regmap_field *field, unsigned int id, } EXPORT_SYMBOL_GPL(regmap_fields_read); +static int _regmap_bulk_read(struct regmap *map, unsigned int reg, + const unsigned int *regs, void *val, size_t val_count) +{ + u32 *u32 = val; + u16 *u16 = val; + u8 *u8 = val; + int ret, i; + + map->lock(map->lock_arg); + + for (i = 0; i < val_count; i++) { + unsigned int ival; + + if (regs) { + if (!IS_ALIGNED(regs[i], map->reg_stride)) { + ret = -EINVAL; + goto out; + } + ret = _regmap_read(map, regs[i], &ival); + } else { + ret = _regmap_read(map, reg + regmap_get_offset(map, i), &ival); + } + if (ret != 0) + goto out; + + switch (map->format.val_bytes) { + case 4: + u32[i] = ival; + break; + case 2: + u16[i] = ival; + break; + case 1: + u8[i] = ival; + break; + default: + ret = -EINVAL; + goto out; + } + } +out: + map->unlock(map->lock_arg); + return ret; +} + /** - * regmap_bulk_read() - Read multiple registers from the device + * regmap_bulk_read() - Read multiple sequential registers from the device * * @map: Register map to read from * @reg: First register to be read from @@ -2804,7 +3186,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, if (val_count == 0) return -EINVAL; - if (map->bus && map->format.parse_inplace && (vol || map->cache_type == REGCACHE_NONE)) { + if (map->read && map->format.parse_inplace && (vol || map->cache_type == REGCACHE_NONE)) { ret = regmap_raw_read(map, reg, val, val_bytes * val_count); if (ret != 0) return ret; @@ -2812,52 +3194,35 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, for (i = 0; i < val_count * val_bytes; i += val_bytes) map->format.parse_inplace(val + i); } else { -#ifdef CONFIG_64BIT - u64 *u64 = val; -#endif - u32 *u32 = val; - u16 *u16 = val; - u8 *u8 = val; - - map->lock(map->lock_arg); - - for (i = 0; i < val_count; i++) { - unsigned int ival; - - ret = _regmap_read(map, reg + regmap_get_offset(map, i), - &ival); - if (ret != 0) - goto out; - - switch (map->format.val_bytes) { -#ifdef CONFIG_64BIT - case 8: - u64[i] = ival; - break; -#endif - case 4: - u32[i] = ival; - break; - case 2: - u16[i] = ival; - break; - case 1: - u8[i] = ival; - break; - default: - ret = -EINVAL; - goto out; - } - } - -out: - map->unlock(map->lock_arg); + ret = _regmap_bulk_read(map, reg, NULL, val, val_count); } - + if (!ret) + trace_regmap_bulk_read(map, reg, val, val_bytes * val_count); return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_read); +/** + * regmap_multi_reg_read() - Read multiple non-sequential registers from the device + * + * @map: Register map to read from + * @regs: Array of registers to read from + * @val: Pointer to store read value, in native register size for device + * @val_count: Number of registers to read + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_multi_reg_read(struct regmap *map, const unsigned int *regs, void *val, + size_t val_count) +{ + if (val_count == 0) + return -EINVAL; + + return _regmap_bulk_read(map, 0, regs, val, val_count); +} +EXPORT_SYMBOL_GPL(regmap_multi_reg_read); + static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change, bool force_write) @@ -2869,6 +3234,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, *change = false; if (regmap_volatile(map, reg) && map->reg_update_bits) { + reg = regmap_reg_addr(map, reg); ret = map->reg_update_bits(map->bus_context, reg, mask, val); if (ret == 0 && change) *change = true; @@ -2880,7 +3246,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, tmp = orig & ~mask; tmp |= val & mask; - if (force_write || (tmp != orig)) { + if (force_write || (tmp != orig) || map->force_write_field) { ret = _regmap_write(map, reg, tmp); if (ret == 0 && change) *change = true; @@ -2932,6 +3298,30 @@ int regmap_update_bits_base(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_update_bits_base); +/** + * regmap_test_bits() - Check if all specified bits are set in a register. + * + * @map: Register map to operate on + * @reg: Register to read from + * @bits: Bits to test + * + * Returns 0 if at least one of the tested bits is not set, 1 if all tested + * bits are set and a negative error number if the underlying regmap_read() + * fails. + */ +int regmap_test_bits(struct regmap *map, unsigned int reg, unsigned int bits) +{ + unsigned int val; + int ret; + + ret = regmap_read(map, reg, &val); + if (ret) + return ret; + + return (val & bits) == bits; +} +EXPORT_SYMBOL_GPL(regmap_test_bits); + void regmap_async_complete_cb(struct regmap_async *async, int ret) { struct regmap *map = async->map; @@ -3083,7 +3473,7 @@ EXPORT_SYMBOL_GPL(regmap_get_val_bytes); */ int regmap_get_max_register(struct regmap *map) { - return map->max_register ? map->max_register : -EINVAL; + return map->max_register_is_set ? map->max_register : -EINVAL; } EXPORT_SYMBOL_GPL(regmap_get_max_register); @@ -3101,6 +3491,19 @@ int regmap_get_reg_stride(struct regmap *map) } EXPORT_SYMBOL_GPL(regmap_get_reg_stride); +/** + * regmap_might_sleep() - Returns whether a regmap access might sleep. + * + * @map: Register map to operate on. + * + * Returns true if an access to the register might sleep, else false. + */ +bool regmap_might_sleep(struct regmap *map) +{ + return map->can_sleep; +} +EXPORT_SYMBOL_GPL(regmap_might_sleep); + int regmap_parse_val(struct regmap *map, const void *buf, unsigned int *val) { |
