diff options
Diffstat (limited to 'drivers/base/regmap')
24 files changed, 1574 insertions, 465 deletions
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index b1affac70d5d..ffb2ef488298 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -6,8 +6,6 @@ config REGMAP bool default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO || REGMAP_FSI) - select IRQ_DOMAIN if REGMAP_IRQ - select MDIO_BUS if REGMAP_MDIO help Enable support for the Register Map (regmap) access API. @@ -58,12 +56,14 @@ config REGMAP_W1 config REGMAP_MDIO tristate + select MDIO_BUS config REGMAP_MMIO tristate config REGMAP_IRQ bool + select IRQ_DOMAIN config REGMAP_RAM tristate diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 583dd5d7d46b..6f31240ee4a9 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -59,6 +59,7 @@ struct regmap { unsigned long raw_spinlock_flags; }; }; + struct lock_class_key *lock_key; regmap_lock lock; regmap_unlock unlock; void *lock_arg; /* This is passed to lock/unlock functions */ @@ -72,12 +73,12 @@ struct regmap { void *bus_context; const char *name; - bool async; spinlock_t async_lock; wait_queue_head_t async_waitq; struct list_head async_list; struct list_head async_free; int async_ret; + bool async; #ifdef CONFIG_DEBUG_FS bool debugfs_disable; @@ -93,6 +94,7 @@ struct regmap { #endif unsigned int max_register; + bool max_register_is_set; bool (*writeable_reg)(struct device *dev, unsigned int reg); bool (*readable_reg)(struct device *dev, unsigned int reg); bool (*volatile_reg)(struct device *dev, unsigned int reg); @@ -115,8 +117,6 @@ struct regmap { void *val_buf, size_t val_size); int (*write)(void *context, const void *data, size_t count); - bool defer_caching; - unsigned long read_flag_mask; unsigned long write_flag_mask; @@ -125,6 +125,8 @@ struct regmap { int reg_stride; int reg_stride_order; + bool defer_caching; + /* If set, will always write field to HW. */ bool force_write_field; @@ -159,6 +161,9 @@ struct regmap { struct reg_sequence *patch; int patch_regs; + /* if set, the regmap core can sleep */ + bool can_sleep; + /* if set, converts bulk read to single read */ bool use_single_read; /* if set, converts bulk write to single write */ @@ -174,9 +179,6 @@ struct regmap { void *selector_work_buf; /* Scratch buffer used for selector */ struct hwspinlock *hwlock; - - /* if set, the regmap core can sleep */ - bool can_sleep; }; struct regcache_ops { @@ -325,20 +327,22 @@ struct regmap_ram_data { * Create a test register map with data stored in RAM, not intended * for practical use. */ -struct regmap *__regmap_init_ram(const struct regmap_config *config, +struct regmap *__regmap_init_ram(struct device *dev, + const struct regmap_config *config, struct regmap_ram_data *data, struct lock_class_key *lock_key, const char *lock_name); -#define regmap_init_ram(config, data) \ - __regmap_lockdep_wrapper(__regmap_init_ram, #config, config, data) +#define regmap_init_ram(dev, config, data) \ + __regmap_lockdep_wrapper(__regmap_init_ram, #dev, dev, config, data) -struct regmap *__regmap_init_raw_ram(const struct regmap_config *config, +struct regmap *__regmap_init_raw_ram(struct device *dev, + const struct regmap_config *config, struct regmap_ram_data *data, struct lock_class_key *lock_key, const char *lock_name); -#define regmap_init_raw_ram(config, data) \ - __regmap_lockdep_wrapper(__regmap_init_raw_ram, #config, config, data) +#define regmap_init_raw_ram(dev, config, data) \ + __regmap_lockdep_wrapper(__regmap_init_raw_ram, #dev, dev, config, data) #endif diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c index b7e4b2464102..f36d3618b67c 100644 --- a/drivers/base/regmap/regcache-flat.c +++ b/drivers/base/regmap/regcache-flat.c @@ -23,11 +23,11 @@ static int regcache_flat_init(struct regmap *map) int i; unsigned int *cache; - if (!map || map->reg_stride_order < 0 || !map->max_register) + if (!map || map->reg_stride_order < 0 || !map->max_register_is_set) return -EINVAL; map->cache = kcalloc(regcache_flat_get_index(map, map->max_register) - + 1, sizeof(unsigned int), GFP_KERNEL); + + 1, sizeof(unsigned int), map->alloc_flags); if (!map->cache) return -ENOMEM; diff --git a/drivers/base/regmap/regcache-maple.c b/drivers/base/regmap/regcache-maple.c index 41edd6a430eb..2319c30283a6 100644 --- a/drivers/base/regmap/regcache-maple.c +++ b/drivers/base/regmap/regcache-maple.c @@ -73,8 +73,7 @@ static int regcache_maple_write(struct regmap *map, unsigned int reg, rcu_read_unlock(); - entry = kmalloc((last - index + 1) * sizeof(unsigned long), - map->alloc_flags); + entry = kmalloc_array(last - index + 1, sizeof(*entry), map->alloc_flags); if (!entry) return -ENOMEM; @@ -110,9 +109,10 @@ static int regcache_maple_drop(struct regmap *map, unsigned int min, struct maple_tree *mt = map->cache; MA_STATE(mas, mt, min, max); unsigned long *entry, *lower, *upper; - unsigned long lower_index, lower_last; + /* initialized to work around false-positive -Wuninitialized warning */ + unsigned long lower_index = 0, lower_last = 0; unsigned long upper_index, upper_last; - int ret; + int ret = 0; lower = NULL; upper = NULL; @@ -132,9 +132,9 @@ static int regcache_maple_drop(struct regmap *map, unsigned int min, lower_index = mas.index; lower_last = min -1; - lower = kmemdup(entry, ((min - mas.index) * - sizeof(unsigned long)), - map->alloc_flags); + lower = kmemdup_array(entry, + min - mas.index, sizeof(*lower), + map->alloc_flags); if (!lower) { ret = -ENOMEM; goto out_unlocked; @@ -145,10 +145,9 @@ static int regcache_maple_drop(struct regmap *map, unsigned int min, upper_index = max + 1; upper_last = mas.last; - upper = kmemdup(&entry[max + 1], - ((mas.last - max) * - sizeof(unsigned long)), - map->alloc_flags); + upper = kmemdup_array(&entry[max - mas.index + 1], + mas.last - max, sizeof(*upper), + map->alloc_flags); if (!upper) { ret = -ENOMEM; goto out_unlocked; @@ -204,7 +203,7 @@ static int regcache_maple_sync_block(struct regmap *map, unsigned long *entry, * overheads. */ if (max - min > 1 && regmap_can_raw_write(map)) { - buf = kmalloc(val_bytes * (max - min), map->alloc_flags); + buf = kmalloc_array(max - min, val_bytes, map->alloc_flags); if (!buf) { ret = -ENOMEM; goto out; @@ -244,7 +243,7 @@ static int regcache_maple_sync(struct regmap *map, unsigned int min, unsigned long lmin = min; unsigned long lmax = max; unsigned int r, v, sync_start; - int ret; + int ret = 0; bool sync_needed = false; map->cache_bypass = true; @@ -294,7 +293,7 @@ static int regcache_maple_exit(struct regmap *map) { struct maple_tree *mt = map->cache; MA_STATE(mas, mt, 0, UINT_MAX); - unsigned int *entry;; + unsigned int *entry; /* if we've already been called then just return */ if (!mt) @@ -320,7 +319,7 @@ static int regcache_maple_insert_block(struct regmap *map, int first, unsigned long *entry; int i, ret; - entry = kcalloc(last - first + 1, sizeof(unsigned long), map->alloc_flags); + entry = kmalloc_array(last - first + 1, sizeof(*entry), map->alloc_flags); if (!entry) return -ENOMEM; @@ -348,13 +347,16 @@ static int regcache_maple_init(struct regmap *map) int ret; int range_start; - mt = kmalloc(sizeof(*mt), GFP_KERNEL); + mt = kmalloc(sizeof(*mt), map->alloc_flags); if (!mt) return -ENOMEM; map->cache = mt; mt_init(mt); + if (!mt_external_lock(mt) && map->lock_key) + lockdep_set_class_and_subclass(&mt->ma_lock, map->lock_key, 1); + if (!map->num_reg_defaults) return 0; diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 3db88bbcae0f..a9d17f316e55 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -187,7 +187,7 @@ static int regcache_rbtree_init(struct regmap *map) int i; int ret; - map->cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL); + map->cache = kmalloc(sizeof *rbtree_ctx, map->alloc_flags); if (!map->cache) return -ENOMEM; @@ -275,18 +275,16 @@ static int regcache_rbtree_insert_to_block(struct regmap *map, pos = (reg - base_reg) / map->reg_stride; offset = (rbnode->base_reg - base_reg) / map->reg_stride; - blk = krealloc(rbnode->block, - blklen * map->cache_word_size, - map->alloc_flags); + blk = krealloc_array(rbnode->block, blklen, map->cache_word_size, map->alloc_flags); if (!blk) return -ENOMEM; rbnode->block = blk; if (BITS_TO_LONGS(blklen) > BITS_TO_LONGS(rbnode->blklen)) { - present = krealloc(rbnode->cache_present, - BITS_TO_LONGS(blklen) * sizeof(*present), - map->alloc_flags); + present = krealloc_array(rbnode->cache_present, + BITS_TO_LONGS(blklen), sizeof(*present), + map->alloc_flags); if (!present) return -ENOMEM; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index ac63a73ccdaa..c7650fa434ad 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -21,6 +21,26 @@ static const struct regcache_ops *cache_types[] = { ®cache_flat_ops, }; +static int regcache_defaults_cmp(const void *a, const void *b) +{ + const struct reg_default *x = a; + const struct reg_default *y = b; + + if (x->reg > y->reg) + return 1; + else if (x->reg < y->reg) + return -1; + else + return 0; +} + +void regcache_sort_defaults(struct reg_default *defaults, unsigned int ndefaults) +{ + sort(defaults, ndefaults, sizeof(*defaults), + regcache_defaults_cmp, NULL); +} +EXPORT_SYMBOL_GPL(regcache_sort_defaults); + static int regcache_hw_init(struct regmap *map) { int i, j; @@ -154,7 +174,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) map->num_reg_defaults = config->num_reg_defaults; map->num_reg_defaults_raw = config->num_reg_defaults_raw; map->reg_defaults_raw = config->reg_defaults_raw; - map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8); + map->cache_word_size = BITS_TO_BYTES(config->val_bits); map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw; map->cache = NULL; @@ -170,8 +190,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) * a copy of it. */ if (config->reg_defaults) { - tmp_buf = kmemdup(config->reg_defaults, map->num_reg_defaults * - sizeof(struct reg_default), GFP_KERNEL); + tmp_buf = kmemdup_array(config->reg_defaults, map->num_reg_defaults, + sizeof(*map->reg_defaults), GFP_KERNEL); if (!tmp_buf) return -ENOMEM; map->reg_defaults = tmp_buf; @@ -187,13 +207,17 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) return 0; } - if (!map->max_register && map->num_reg_defaults_raw) + if (!map->max_register_is_set && map->num_reg_defaults_raw) { map->max_register = (map->num_reg_defaults_raw - 1) * map->reg_stride; + map->max_register_is_set = true; + } if (map->cache_ops->init) { dev_dbg(map->dev, "Initializing %s cache\n", map->cache_ops->name); + map->lock(map->lock_arg); ret = map->cache_ops->init(map); + map->unlock(map->lock_arg); if (ret) goto err_free; } @@ -221,7 +245,9 @@ void regcache_exit(struct regmap *map) if (map->cache_ops->exit) { dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name); + map->lock(map->lock_arg); map->cache_ops->exit(map); + map->unlock(map->lock_arg); } } @@ -405,7 +431,7 @@ out: * have gone out of sync, force writes of all the paging * registers. */ - rb_for_each(node, 0, &map->range_tree, rbtree_all) { + rb_for_each(node, NULL, &map->range_tree, rbtree_all) { struct regmap_range_node *this = rb_entry(node, struct regmap_range_node, node); diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c index b9f76bdf74a9..a561971c459c 100644 --- a/drivers/base/regmap/regmap-ac97.c +++ b/drivers/base/regmap/regmap-ac97.c @@ -86,4 +86,5 @@ struct regmap *__devm_regmap_init_ac97(struct snd_ac97 *ac97, } EXPORT_SYMBOL_GPL(__devm_regmap_init_ac97); +MODULE_DESCRIPTION("Register map access API - AC'97 support"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 3ec611dc0c09..c9b39a02278e 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -350,7 +350,8 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, if (quirks->max_write_len && (bus->max_raw_write == 0 || bus->max_raw_write > quirks->max_write_len)) - max_write = quirks->max_write_len; + max_write = quirks->max_write_len - + (config->reg_bits + config->pad_bits) / BITS_PER_BYTE; if (max_read || max_write) { ret_bus = kmemdup(bus, sizeof(*bus), GFP_KERNEL); @@ -396,4 +397,5 @@ struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c, } EXPORT_SYMBOL_GPL(__devm_regmap_init_i2c); +MODULE_DESCRIPTION("Register map access API - I2C support"); MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap-i3c.c b/drivers/base/regmap/regmap-i3c.c index 0328b0b34284..b5300b7c477e 100644 --- a/drivers/base/regmap/regmap-i3c.c +++ b/drivers/base/regmap/regmap-i3c.c @@ -56,5 +56,5 @@ struct regmap *__devm_regmap_init_i3c(struct i3c_device *i3c, EXPORT_SYMBOL_GPL(__devm_regmap_init_i3c); MODULE_AUTHOR("Vitor Soares <vitor.soares@synopsys.com>"); -MODULE_DESCRIPTION("Regmap I3C Module"); +MODULE_DESCRIPTION("regmap I3C Module"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 45fd13ef13fc..d1585f073776 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -6,11 +6,13 @@ // // Author: Mark Brown <broonie@opensource.wolfsonmicro.com> +#include <linux/array_size.h> #include <linux/device.h> #include <linux/export.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqdomain.h> +#include <linux/overflow.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -33,6 +35,7 @@ struct regmap_irq_chip_data { void *status_reg_buf; unsigned int *main_status_buf; unsigned int *status_buf; + unsigned int *prev_status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; unsigned int *wake_buf; @@ -193,10 +196,10 @@ static void regmap_irq_sync_unlock(struct irq_data *data) /* If we've changed our wakeup count propagate it to the parent */ if (d->wake_count < 0) for (i = d->wake_count; i < 0; i++) - irq_set_irq_wake(d->irq, 0); + disable_irq_wake(d->irq); else if (d->wake_count > 0) for (i = 0; i < d->wake_count; i++) - irq_set_irq_wake(d->irq, 1); + enable_irq_wake(d->irq); d->wake_count = 0; @@ -305,8 +308,8 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data, unsigned int b) { const struct regmap_irq_chip *chip = data->chip; + const struct regmap_irq_sub_irq_map *subreg; struct regmap *map = data->map; - struct regmap_irq_sub_irq_map *subreg; unsigned int reg; int i, ret = 0; @@ -332,27 +335,13 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data, return ret; } -static irqreturn_t regmap_irq_thread(int irq, void *d) +static int read_irq_data(struct regmap_irq_chip_data *data) { - struct regmap_irq_chip_data *data = d; const struct regmap_irq_chip *chip = data->chip; struct regmap *map = data->map; int ret, i; - bool handled = false; u32 reg; - if (chip->handle_pre_irq) - chip->handle_pre_irq(chip->irq_drv_data); - - if (chip->runtime_pm) { - ret = pm_runtime_get_sync(map->dev); - if (ret < 0) { - dev_err(map->dev, "IRQ thread failed to resume: %d\n", - ret); - goto exit; - } - } - /* * Read only registers with active IRQs if the chip has 'main status * register'. Else read in the statuses, using a single bulk read if @@ -364,14 +353,11 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) memset32(data->status_buf, GENMASK(31, 0), chip->num_regs); } else if (chip->num_main_regs) { unsigned int max_main_bits; - unsigned long size; - - size = chip->num_regs * sizeof(unsigned int); max_main_bits = (chip->num_main_status_bits) ? chip->num_main_status_bits : chip->num_regs; /* Clear the status buf as we don't read all status regs */ - memset(data->status_buf, 0, size); + memset32(data->status_buf, 0, chip->num_regs); /* We could support bulk read for main status registers * but I don't expect to see devices with really many main @@ -382,10 +368,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) reg = data->get_irq_reg(data, chip->main_status, i); ret = regmap_read(map, reg, &data->main_status_buf[i]); if (ret) { - dev_err(map->dev, - "Failed to read IRQ status %d\n", - ret); - goto exit; + dev_err(map->dev, "Failed to read IRQ status %d\n", ret); + return ret; } } @@ -401,10 +385,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) ret = read_sub_irq_data(data, b); if (ret != 0) { - dev_err(map->dev, - "Failed to read IRQ status %d\n", - ret); - goto exit; + dev_err(map->dev, "Failed to read IRQ status %d\n", ret); + return ret; } } @@ -421,9 +403,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) data->status_reg_buf, chip->num_regs); if (ret != 0) { - dev_err(map->dev, "Failed to read IRQ status: %d\n", - ret); - goto exit; + dev_err(map->dev, "Failed to read IRQ status: %d\n", ret); + return ret; } for (i = 0; i < data->chip->num_regs; i++) { @@ -439,7 +420,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) break; default: BUG(); - goto exit; + return -EIO; } } @@ -450,10 +431,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) ret = regmap_read(map, reg, &data->status_buf[i]); if (ret != 0) { - dev_err(map->dev, - "Failed to read IRQ status: %d\n", - ret); - goto exit; + dev_err(map->dev, "Failed to read IRQ status: %d\n", ret); + return ret; } } } @@ -462,6 +441,42 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) for (i = 0; i < data->chip->num_regs; i++) data->status_buf[i] = ~data->status_buf[i]; + return 0; +} + +static irqreturn_t regmap_irq_thread(int irq, void *d) +{ + struct regmap_irq_chip_data *data = d; + const struct regmap_irq_chip *chip = data->chip; + struct regmap *map = data->map; + int ret, i; + bool handled = false; + u32 reg; + + if (chip->handle_pre_irq) + chip->handle_pre_irq(chip->irq_drv_data); + + if (chip->runtime_pm) { + ret = pm_runtime_get_sync(map->dev); + if (ret < 0) { + dev_err(map->dev, "IRQ thread failed to resume: %d\n", ret); + goto exit; + } + } + + ret = read_irq_data(data); + if (ret < 0) + goto exit; + + if (chip->status_is_level) { + for (i = 0; i < data->chip->num_regs; i++) { + unsigned int val = data->status_buf[i]; + + data->status_buf[i] ^= data->prev_status_buf[i]; + data->prev_status_buf[i] = val; + } + } + /* * Ignore masked IRQs and ack if we need to; we ack early so * there is no race between handling and acknowledging the @@ -514,12 +529,16 @@ exit: return IRQ_NONE; } +static struct lock_class_key regmap_irq_lock_class; +static struct lock_class_key regmap_irq_request_class; + static int regmap_irq_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { struct regmap_irq_chip_data *data = h->host_data; irq_set_chip_data(virq, data); + irq_set_lockdep_class(virq, ®map_irq_lock_class, ®map_irq_request_class); irq_set_chip(virq, &data->irq_chip); irq_set_nested_thread(virq, 1); irq_set_parent(virq, data->irq); @@ -608,6 +627,30 @@ int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type, } EXPORT_SYMBOL_GPL(regmap_irq_set_type_config_simple); +static int regmap_irq_create_domain(struct fwnode_handle *fwnode, int irq_base, + const struct regmap_irq_chip *chip, + struct regmap_irq_chip_data *d) +{ + struct irq_domain_info info = { + .fwnode = fwnode, + .size = chip->num_irqs, + .hwirq_max = chip->num_irqs, + .virq_base = irq_base, + .ops = ®map_domain_ops, + .host_data = d, + .name_suffix = chip->domain_suffix, + }; + + d->domain = irq_domain_instantiate(&info); + if (IS_ERR(d->domain)) { + dev_err(d->map->dev, "Failed to create IRQ domain\n"); + return PTR_ERR(d->domain); + } + + return 0; +} + + /** * regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling * @@ -680,6 +723,13 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (!d->status_buf) goto err_alloc; + if (chip->status_is_level) { + d->prev_status_buf = kcalloc(chip->num_regs, sizeof(*d->prev_status_buf), + GFP_KERNEL); + if (!d->prev_status_buf) + goto err_alloc; + } + d->mask_buf = kcalloc(chip->num_regs, sizeof(*d->mask_buf), GFP_KERNEL); if (!d->mask_buf) @@ -798,7 +848,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, /* Ack masked but set interrupts */ if (d->chip->no_status) { /* no status register so default to all active */ - d->status_buf[i] = GENMASK(31, 0); + d->status_buf[i] = UINT_MAX; } else { reg = d->get_irq_reg(d, d->chip->status_base, i); ret = regmap_read(map, reg, &d->status_buf[i]); @@ -856,19 +906,20 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, } } - if (irq_base) - d->domain = irq_domain_create_legacy(fwnode, chip->num_irqs, - irq_base, 0, - ®map_domain_ops, d); - else - d->domain = irq_domain_create_linear(fwnode, chip->num_irqs, - ®map_domain_ops, d); - if (!d->domain) { - dev_err(map->dev, "Failed to create IRQ domain\n"); - ret = -ENOMEM; - goto err_alloc; + /* Store current levels */ + if (chip->status_is_level) { + ret = read_irq_data(d); + if (ret < 0) + goto err_alloc; + + memcpy(d->prev_status_buf, d->status_buf, + array_size(d->chip->num_regs, sizeof(d->prev_status_buf[0]))); } + ret = regmap_irq_create_domain(fwnode, irq_base, chip, d); + if (ret) + goto err_alloc; + ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags | IRQF_ONESHOT, chip->name, d); @@ -890,7 +941,9 @@ err_alloc: kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); + kfree(d->main_status_buf); kfree(d->status_buf); + kfree(d->prev_status_buf); kfree(d->status_reg_buf); if (d->config_buf) { for (i = 0; i < chip->num_config_bases; i++) @@ -965,8 +1018,10 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); + kfree(d->main_status_buf); kfree(d->status_reg_buf); kfree(d->status_buf); + kfree(d->prev_status_buf); if (d->config_buf) { for (i = 0; i < d->chip->num_config_bases; i++) kfree(d->config_buf[i]); diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c index 026bdcb45127..64ea340950b6 100644 --- a/drivers/base/regmap/regmap-kunit.c +++ b/drivers/base/regmap/regmap-kunit.c @@ -4,63 +4,179 @@ // // Copyright 2023 Arm Ltd +#include <kunit/device.h> +#include <kunit/resource.h> #include <kunit/test.h> #include "internal.h" #define BLOCK_TEST_SIZE 12 +KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_action, regmap_exit, struct regmap *); + +struct regmap_test_priv { + struct device *dev; +}; + +struct regmap_test_param { + enum regcache_type cache; + enum regmap_endian val_endian; + + unsigned int from_reg; + bool fast_io; +}; + +static void get_changed_bytes(void *orig, void *new, size_t size) +{ + char *o = orig; + char *n = new; + int i; + + get_random_bytes(new, size); + + /* + * This could be nicer and more efficient but we shouldn't + * super care. + */ + for (i = 0; i < size; i++) + while (n[i] == o[i]) + get_random_bytes(&n[i], 1); +} + static const struct regmap_config test_regmap_config = { - .max_register = BLOCK_TEST_SIZE, .reg_stride = 1, .val_bits = sizeof(unsigned int) * 8, }; -struct regcache_types { - enum regcache_type type; - const char *name; -}; +static const char *regcache_type_name(enum regcache_type type) +{ + switch (type) { + case REGCACHE_NONE: + return "none"; + case REGCACHE_FLAT: + return "flat"; + case REGCACHE_RBTREE: + return "rbtree"; + case REGCACHE_MAPLE: + return "maple"; + default: + return NULL; + } +} + +static const char *regmap_endian_name(enum regmap_endian endian) +{ + switch (endian) { + case REGMAP_ENDIAN_BIG: + return "big"; + case REGMAP_ENDIAN_LITTLE: + return "little"; + case REGMAP_ENDIAN_DEFAULT: + return "default"; + case REGMAP_ENDIAN_NATIVE: + return "native"; + default: + return NULL; + } +} -static void case_to_desc(const struct regcache_types *t, char *desc) +static void param_to_desc(const struct regmap_test_param *param, char *desc) { - strcpy(desc, t->name); + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s-%s%s @%#x", + regcache_type_name(param->cache), + regmap_endian_name(param->val_endian), + param->fast_io ? " fast I/O" : "", + param->from_reg); } -static const struct regcache_types regcache_types_list[] = { - { REGCACHE_NONE, "none" }, - { REGCACHE_FLAT, "flat" }, - { REGCACHE_RBTREE, "rbtree" }, - { REGCACHE_MAPLE, "maple" }, +static const struct regmap_test_param regcache_types_list[] = { + { .cache = REGCACHE_NONE }, + { .cache = REGCACHE_NONE, .fast_io = true }, + { .cache = REGCACHE_FLAT }, + { .cache = REGCACHE_FLAT, .fast_io = true }, + { .cache = REGCACHE_RBTREE }, + { .cache = REGCACHE_RBTREE, .fast_io = true }, + { .cache = REGCACHE_MAPLE }, + { .cache = REGCACHE_MAPLE, .fast_io = true }, }; -KUNIT_ARRAY_PARAM(regcache_types, regcache_types_list, case_to_desc); +KUNIT_ARRAY_PARAM(regcache_types, regcache_types_list, param_to_desc); -static const struct regcache_types real_cache_types_list[] = { - { REGCACHE_FLAT, "flat" }, - { REGCACHE_RBTREE, "rbtree" }, - { REGCACHE_MAPLE, "maple" }, +static const struct regmap_test_param real_cache_types_only_list[] = { + { .cache = REGCACHE_FLAT }, + { .cache = REGCACHE_FLAT, .fast_io = true }, + { .cache = REGCACHE_RBTREE }, + { .cache = REGCACHE_RBTREE, .fast_io = true }, + { .cache = REGCACHE_MAPLE }, + { .cache = REGCACHE_MAPLE, .fast_io = true }, }; -KUNIT_ARRAY_PARAM(real_cache_types, real_cache_types_list, case_to_desc); +KUNIT_ARRAY_PARAM(real_cache_types_only, real_cache_types_only_list, param_to_desc); + +static const struct regmap_test_param real_cache_types_list[] = { + { .cache = REGCACHE_FLAT, .from_reg = 0 }, + { .cache = REGCACHE_FLAT, .from_reg = 0, .fast_io = true }, + { .cache = REGCACHE_FLAT, .from_reg = 0x2001 }, + { .cache = REGCACHE_FLAT, .from_reg = 0x2002 }, + { .cache = REGCACHE_FLAT, .from_reg = 0x2003 }, + { .cache = REGCACHE_FLAT, .from_reg = 0x2004 }, + { .cache = REGCACHE_RBTREE, .from_reg = 0 }, + { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, + { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, + { .cache = REGCACHE_RBTREE, .from_reg = 0x2002 }, + { .cache = REGCACHE_RBTREE, .from_reg = 0x2003 }, + { .cache = REGCACHE_RBTREE, .from_reg = 0x2004 }, + { .cache = REGCACHE_MAPLE, .from_reg = 0 }, + { .cache = REGCACHE_MAPLE, .from_reg = 0, .fast_io = true }, + { .cache = REGCACHE_MAPLE, .from_reg = 0x2001 }, + { .cache = REGCACHE_MAPLE, .from_reg = 0x2002 }, + { .cache = REGCACHE_MAPLE, .from_reg = 0x2003 }, + { .cache = REGCACHE_MAPLE, .from_reg = 0x2004 }, +}; -static const struct regcache_types sparse_cache_types_list[] = { - { REGCACHE_RBTREE, "rbtree" }, - { REGCACHE_MAPLE, "maple" }, +KUNIT_ARRAY_PARAM(real_cache_types, real_cache_types_list, param_to_desc); + +static const struct regmap_test_param sparse_cache_types_list[] = { + { .cache = REGCACHE_RBTREE, .from_reg = 0 }, + { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, + { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, + { .cache = REGCACHE_RBTREE, .from_reg = 0x2002 }, + { .cache = REGCACHE_RBTREE, .from_reg = 0x2003 }, + { .cache = REGCACHE_RBTREE, .from_reg = 0x2004 }, + { .cache = REGCACHE_MAPLE, .from_reg = 0 }, + { .cache = REGCACHE_MAPLE, .from_reg = 0, .fast_io = true }, + { .cache = REGCACHE_MAPLE, .from_reg = 0x2001 }, + { .cache = REGCACHE_MAPLE, .from_reg = 0x2002 }, + { .cache = REGCACHE_MAPLE, .from_reg = 0x2003 }, + { .cache = REGCACHE_MAPLE, .from_reg = 0x2004 }, }; -KUNIT_ARRAY_PARAM(sparse_cache_types, sparse_cache_types_list, case_to_desc); +KUNIT_ARRAY_PARAM(sparse_cache_types, sparse_cache_types_list, param_to_desc); -static struct regmap *gen_regmap(struct regmap_config *config, +static struct regmap *gen_regmap(struct kunit *test, + struct regmap_config *config, struct regmap_ram_data **data) { + const struct regmap_test_param *param = test->param_value; + struct regmap_test_priv *priv = test->priv; unsigned int *buf; - struct regmap *ret; - size_t size = (config->max_register + 1) * sizeof(unsigned int); - int i; + struct regmap *ret = ERR_PTR(-ENOMEM); + size_t size; + int i, error; struct reg_default *defaults; - config->disable_locking = config->cache_type == REGCACHE_RBTREE || - config->cache_type == REGCACHE_MAPLE; + config->cache_type = param->cache; + config->fast_io = param->fast_io; + + if (config->max_register == 0) { + config->max_register = param->from_reg; + if (config->num_reg_defaults) + config->max_register += (config->num_reg_defaults - 1) * + config->reg_stride; + else + config->max_register += (BLOCK_TEST_SIZE * config->reg_stride); + } + size = array_size(config->max_register + 1, sizeof(*buf)); buf = kmalloc(size, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); @@ -69,49 +185,61 @@ static struct regmap *gen_regmap(struct regmap_config *config, *data = kzalloc(sizeof(**data), GFP_KERNEL); if (!(*data)) - return ERR_PTR(-ENOMEM); + goto out_free; (*data)->vals = buf; if (config->num_reg_defaults) { - defaults = kcalloc(config->num_reg_defaults, - sizeof(struct reg_default), - GFP_KERNEL); + defaults = kunit_kcalloc(test, + config->num_reg_defaults, + sizeof(struct reg_default), + GFP_KERNEL); if (!defaults) - return ERR_PTR(-ENOMEM); + goto out_free; + config->reg_defaults = defaults; for (i = 0; i < config->num_reg_defaults; i++) { - defaults[i].reg = i * config->reg_stride; - defaults[i].def = buf[i * config->reg_stride]; + defaults[i].reg = param->from_reg + (i * config->reg_stride); + defaults[i].def = buf[param->from_reg + (i * config->reg_stride)]; } } - ret = regmap_init_ram(config, *data); - if (IS_ERR(ret)) { - kfree(buf); - kfree(*data); - } + ret = regmap_init_ram(priv->dev, config, *data); + if (IS_ERR(ret)) + goto out_free; + + /* This calls regmap_exit() on failure, which frees buf and *data */ + error = kunit_add_action_or_reset(test, regmap_exit_action, ret); + if (error) + ret = ERR_PTR(error); + + return ret; + +out_free: + kfree(buf); + kfree(*data); return ret; } -static bool reg_5_false(struct device *context, unsigned int reg) +static bool reg_5_false(struct device *dev, unsigned int reg) { - return reg != 5; + struct kunit *test = dev_get_drvdata(dev); + const struct regmap_test_param *param = test->param_value; + + return reg != (param->from_reg + 5); } static void basic_read_write(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; unsigned int val, rval; config = test_regmap_config; - config.cache_type = t->type; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -124,14 +252,11 @@ static void basic_read_write(struct kunit *test) KUNIT_EXPECT_EQ(test, val, rval); /* If using a cache the cache satisfied the read */ - KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[0]); - - regmap_exit(map); + KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[0]); } static void bulk_write(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -139,9 +264,8 @@ static void bulk_write(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -161,14 +285,11 @@ static void bulk_write(struct kunit *test) /* If using a cache the cache satisfied the read */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[i]); - - regmap_exit(map); + KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]); } static void bulk_read(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -176,9 +297,8 @@ static void bulk_read(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -194,14 +314,211 @@ static void bulk_read(struct kunit *test) /* If using a cache the cache satisfied the read */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[i]); + KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]); +} + +static void multi_write(struct kunit *test) +{ + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + struct reg_sequence sequence[BLOCK_TEST_SIZE]; + unsigned int val[BLOCK_TEST_SIZE], rval[BLOCK_TEST_SIZE]; + int i; + + config = test_regmap_config; + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + get_random_bytes(&val, sizeof(val)); + + /* + * Data written via the multi API can be read back with single + * reads. + */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) { + sequence[i].reg = i; + sequence[i].def = val[i]; + sequence[i].delay_us = 0; + } + KUNIT_EXPECT_EQ(test, 0, + regmap_multi_reg_write(map, sequence, BLOCK_TEST_SIZE)); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval[i])); - regmap_exit(map); + KUNIT_EXPECT_MEMEQ(test, val, rval, sizeof(val)); + + /* If using a cache the cache satisfied the read */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]); +} + +static void multi_read(struct kunit *test) +{ + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int regs[BLOCK_TEST_SIZE]; + unsigned int val[BLOCK_TEST_SIZE], rval[BLOCK_TEST_SIZE]; + int i; + + config = test_regmap_config; + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + get_random_bytes(&val, sizeof(val)); + + /* Data written as single writes can be read via the multi API */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) { + regs[i] = i; + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, val[i])); + } + KUNIT_EXPECT_EQ(test, 0, + regmap_multi_reg_read(map, regs, rval, BLOCK_TEST_SIZE)); + KUNIT_EXPECT_MEMEQ(test, val, rval, sizeof(val)); + + /* If using a cache the cache satisfied the read */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]); +} + +static void read_bypassed(struct kunit *test) +{ + const struct regmap_test_param *param = test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val[BLOCK_TEST_SIZE], rval; + int i; + + config = test_regmap_config; + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + KUNIT_EXPECT_FALSE(test, map->cache_bypass); + + get_random_bytes(&val, sizeof(val)); + + /* Write some test values */ + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val, ARRAY_SIZE(val))); + + regcache_cache_only(map, true); + + /* + * While in cache-only regmap_read_bypassed() should return the register + * value and leave the map in cache-only. + */ + for (i = 0; i < ARRAY_SIZE(val); i++) { + /* Put inverted bits in rval to prove we really read the value */ + rval = ~val[i]; + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &rval)); + KUNIT_EXPECT_EQ(test, val[i], rval); + + rval = ~val[i]; + KUNIT_EXPECT_EQ(test, 0, regmap_read_bypassed(map, param->from_reg + i, &rval)); + KUNIT_EXPECT_EQ(test, val[i], rval); + KUNIT_EXPECT_TRUE(test, map->cache_only); + KUNIT_EXPECT_FALSE(test, map->cache_bypass); + } + + /* + * Change the underlying register values to prove it is returning + * real values not cached values. + */ + for (i = 0; i < ARRAY_SIZE(val); i++) { + val[i] = ~val[i]; + data->vals[param->from_reg + i] = val[i]; + } + + for (i = 0; i < ARRAY_SIZE(val); i++) { + rval = ~val[i]; + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &rval)); + KUNIT_EXPECT_NE(test, val[i], rval); + + rval = ~val[i]; + KUNIT_EXPECT_EQ(test, 0, regmap_read_bypassed(map, param->from_reg + i, &rval)); + KUNIT_EXPECT_EQ(test, val[i], rval); + KUNIT_EXPECT_TRUE(test, map->cache_only); + KUNIT_EXPECT_FALSE(test, map->cache_bypass); + } +} + +static void read_bypassed_volatile(struct kunit *test) +{ + const struct regmap_test_param *param = test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val[BLOCK_TEST_SIZE], rval; + int i; + + config = test_regmap_config; + /* All registers except #5 volatile */ + config.volatile_reg = reg_5_false; + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + KUNIT_EXPECT_FALSE(test, map->cache_bypass); + + get_random_bytes(&val, sizeof(val)); + + /* Write some test values */ + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val, ARRAY_SIZE(val))); + + regcache_cache_only(map, true); + + /* + * While in cache-only regmap_read_bypassed() should return the register + * value and leave the map in cache-only. + */ + for (i = 0; i < ARRAY_SIZE(val); i++) { + /* Register #5 is non-volatile so should read from cache */ + KUNIT_EXPECT_EQ(test, (i == 5) ? 0 : -EBUSY, + regmap_read(map, param->from_reg + i, &rval)); + + /* Put inverted bits in rval to prove we really read the value */ + rval = ~val[i]; + KUNIT_EXPECT_EQ(test, 0, regmap_read_bypassed(map, param->from_reg + i, &rval)); + KUNIT_EXPECT_EQ(test, val[i], rval); + KUNIT_EXPECT_TRUE(test, map->cache_only); + KUNIT_EXPECT_FALSE(test, map->cache_bypass); + } + + /* + * Change the underlying register values to prove it is returning + * real values not cached values. + */ + for (i = 0; i < ARRAY_SIZE(val); i++) { + val[i] = ~val[i]; + data->vals[param->from_reg + i] = val[i]; + } + + for (i = 0; i < ARRAY_SIZE(val); i++) { + if (i == 5) + continue; + + rval = ~val[i]; + KUNIT_EXPECT_EQ(test, 0, regmap_read_bypassed(map, param->from_reg + i, &rval)); + KUNIT_EXPECT_EQ(test, val[i], rval); + KUNIT_EXPECT_TRUE(test, map->cache_only); + KUNIT_EXPECT_FALSE(test, map->cache_bypass); + } } static void write_readonly(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -209,11 +526,10 @@ static void write_readonly(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; config.num_reg_defaults = BLOCK_TEST_SIZE; config.writeable_reg = reg_5_false; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -230,13 +546,10 @@ static void write_readonly(struct kunit *test) /* Did that match what we see on the device? */ for (i = 0; i < BLOCK_TEST_SIZE; i++) KUNIT_EXPECT_EQ(test, i != 5, data->written[i]); - - regmap_exit(map); } static void read_writeonly(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -244,10 +557,9 @@ static void read_writeonly(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; config.readable_reg = reg_5_false; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -260,7 +572,7 @@ static void read_writeonly(struct kunit *test) * fail if we aren't using the flat cache. */ for (i = 0; i < BLOCK_TEST_SIZE; i++) { - if (t->type != REGCACHE_FLAT) { + if (config.cache_type != REGCACHE_FLAT) { KUNIT_EXPECT_EQ(test, i != 5, regmap_read(map, i, &val) == 0); } else { @@ -270,13 +582,10 @@ static void read_writeonly(struct kunit *test) /* Did we trigger a hardware access? */ KUNIT_EXPECT_FALSE(test, data->read[5]); - - regmap_exit(map); } static void reg_defaults(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -284,10 +593,9 @@ static void reg_defaults(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; config.num_reg_defaults = BLOCK_TEST_SIZE; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -299,12 +607,11 @@ static void reg_defaults(struct kunit *test) /* The data should have been read from cache if there was one */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[i]); + KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]); } static void reg_defaults_read_dev(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -312,17 +619,16 @@ static void reg_defaults_read_dev(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; config.num_reg_defaults_raw = BLOCK_TEST_SIZE; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; /* We should have read the cache defaults back from the map */ for (i = 0; i < BLOCK_TEST_SIZE; i++) { - KUNIT_EXPECT_EQ(test, t->type != REGCACHE_NONE, data->read[i]); + KUNIT_EXPECT_EQ(test, config.cache_type != REGCACHE_NONE, data->read[i]); data->read[i] = false; } @@ -333,12 +639,11 @@ static void reg_defaults_read_dev(struct kunit *test) /* The data should have been read from cache if there was one */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[i]); + KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]); } static void register_patch(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -348,10 +653,9 @@ static void register_patch(struct kunit *test) /* We need defaults so readback works */ config = test_regmap_config; - config.cache_type = t->type; config.num_reg_defaults = BLOCK_TEST_SIZE; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -384,13 +688,10 @@ static void register_patch(struct kunit *test) break; } } - - regmap_exit(map); } static void stride(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -398,16 +699,22 @@ static void stride(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; config.reg_stride = 2; config.num_reg_defaults = BLOCK_TEST_SIZE / 2; - map = gen_regmap(&config, &data); + /* + * Allow one extra register so that the read/written arrays + * are sized big enough to include an entry for the odd + * address past the final reg_default register. + */ + config.max_register = BLOCK_TEST_SIZE; + + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; - /* Only even registers can be accessed, try both read and write */ + /* Only even addresses can be accessed, try both read and write */ for (i = 0; i < BLOCK_TEST_SIZE; i++) { data->read[i] = false; data->written[i] = false; @@ -420,15 +727,13 @@ static void stride(struct kunit *test) } else { KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval)); KUNIT_EXPECT_EQ(test, data->vals[i], rval); - KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, + KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]); KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, rval)); KUNIT_EXPECT_TRUE(test, data->written[i]); } } - - regmap_exit(map); } static struct regmap_range_cfg test_range = { @@ -464,7 +769,6 @@ static bool test_range_all_volatile(struct device *dev, unsigned int reg) static void basic_ranges(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -472,13 +776,12 @@ static void basic_ranges(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; config.volatile_reg = test_range_all_volatile; config.ranges = &test_range; config.num_ranges = 1; config.max_register = test_range.range_max; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -529,14 +832,11 @@ static void basic_ranges(struct kunit *test) KUNIT_EXPECT_FALSE(test, data->read[i]); KUNIT_EXPECT_FALSE(test, data->written[i]); } - - regmap_exit(map); } /* Try to stress dynamic creation of cache data structures */ static void stress_insert(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -545,18 +845,16 @@ static void stress_insert(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; config.max_register = 300; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; - vals = kunit_kcalloc(test, sizeof(unsigned long), config.max_register, - GFP_KERNEL); + buf_sz = array_size(sizeof(*vals), config.max_register); + vals = kunit_kmalloc(test, buf_sz, GFP_KERNEL); KUNIT_ASSERT_FALSE(test, vals == NULL); - buf_sz = sizeof(unsigned long) * config.max_register; get_random_bytes(vals, buf_sz); @@ -582,24 +880,21 @@ static void stress_insert(struct kunit *test) for (i = 0; i < config.max_register; i ++) { KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval)); KUNIT_EXPECT_EQ(test, rval, vals[i]); - KUNIT_EXPECT_EQ(test, t->type == REGCACHE_NONE, data->read[i]); + KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]); } - - regmap_exit(map); } static void cache_bypass(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; + const struct regmap_test_param *param = test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; unsigned int val, rval; config = test_regmap_config; - config.cache_type = t->type; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -607,28 +902,26 @@ static void cache_bypass(struct kunit *test) get_random_bytes(&val, sizeof(val)); /* Ensure the cache has a value in it */ - KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val)); + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg, val)); /* Bypass then write a different value */ regcache_cache_bypass(map, true); - KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val + 1)); + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg, val + 1)); /* Read the bypassed value */ - KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval)); + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg, &rval)); KUNIT_EXPECT_EQ(test, val + 1, rval); - KUNIT_EXPECT_EQ(test, data->vals[0], rval); + KUNIT_EXPECT_EQ(test, data->vals[param->from_reg], rval); /* Disable bypass, the cache should still return the original value */ regcache_cache_bypass(map, false); - KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval)); + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg, &rval)); KUNIT_EXPECT_EQ(test, val, rval); - - regmap_exit(map); } -static void cache_sync(struct kunit *test) +static void cache_sync_marked_dirty(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; + const struct regmap_test_param *param = test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -636,9 +929,8 @@ static void cache_sync(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -646,10 +938,10 @@ static void cache_sync(struct kunit *test) get_random_bytes(&val, sizeof(val)); /* Put some data into the cache */ - KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, 0, val, + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val, BLOCK_TEST_SIZE)); for (i = 0; i < BLOCK_TEST_SIZE; i++) - data->written[i] = false; + data->written[param->from_reg + i] = false; /* Trash the data on the device itself then resync */ regcache_mark_dirty(map); @@ -657,16 +949,63 @@ static void cache_sync(struct kunit *test) KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); /* Did we just write the correct data out? */ - KUNIT_EXPECT_MEMEQ(test, data->vals, val, sizeof(val)); + KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], val, sizeof(val)); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, true, data->written[param->from_reg + i]); +} + +static void cache_sync_after_cache_only(struct kunit *test) +{ + const struct regmap_test_param *param = test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val[BLOCK_TEST_SIZE]; + unsigned int val_mask; + int i; + + config = test_regmap_config; + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + val_mask = GENMASK(config.val_bits - 1, 0); + get_random_bytes(&val, sizeof(val)); + + /* Put some data into the cache */ + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val, + BLOCK_TEST_SIZE)); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->written[param->from_reg + i] = false; + + /* Set cache-only and change the values */ + regcache_cache_only(map, true); + for (i = 0; i < ARRAY_SIZE(val); ++i) + val[i] = ~val[i] & val_mask; + + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val, + BLOCK_TEST_SIZE)); for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_EXPECT_EQ(test, true, data->written[i]); + KUNIT_EXPECT_FALSE(test, data->written[param->from_reg + i]); + + KUNIT_EXPECT_MEMNEQ(test, &data->vals[param->from_reg], val, sizeof(val)); - regmap_exit(map); + /* Exit cache-only and sync the cache without marking hardware registers dirty */ + regcache_cache_only(map, false); + + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + + /* Did we just write the correct data out? */ + KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], val, sizeof(val)); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_TRUE(test, data->written[param->from_reg + i]); } -static void cache_sync_defaults(struct kunit *test) +static void cache_sync_defaults_marked_dirty(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; + const struct regmap_test_param *param = test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -674,10 +1013,9 @@ static void cache_sync_defaults(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; config.num_reg_defaults = BLOCK_TEST_SIZE; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -685,24 +1023,85 @@ static void cache_sync_defaults(struct kunit *test) get_random_bytes(&val, sizeof(val)); /* Change the value of one register */ - KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 2, val)); + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + 2, val)); /* Resync */ regcache_mark_dirty(map); for (i = 0; i < BLOCK_TEST_SIZE; i++) - data->written[i] = false; + data->written[param->from_reg + i] = false; KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); /* Did we just sync the one register we touched? */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_EXPECT_EQ(test, i == 2, data->written[i]); + KUNIT_EXPECT_EQ(test, i == 2, data->written[param->from_reg + i]); + + /* Rewrite registers back to their defaults */ + for (i = 0; i < config.num_reg_defaults; ++i) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, config.reg_defaults[i].reg, + config.reg_defaults[i].def)); + + /* + * Resync after regcache_mark_dirty() should not write out registers + * that are at default value + */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->written[param->from_reg + i] = false; + regcache_mark_dirty(map); + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_FALSE(test, data->written[param->from_reg + i]); +} + +static void cache_sync_default_after_cache_only(struct kunit *test) +{ + const struct regmap_test_param *param = test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int orig_val; + int i; + + config = test_regmap_config; + config.num_reg_defaults = BLOCK_TEST_SIZE; + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + 2, &orig_val)); + + /* Enter cache-only and change the value of one register */ + regcache_cache_only(map, true); + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + 2, orig_val + 1)); + + /* Exit cache-only and resync, should write out the changed register */ + regcache_cache_only(map, false); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->written[param->from_reg + i] = false; + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + + /* Was the register written out? */ + KUNIT_EXPECT_TRUE(test, data->written[param->from_reg + 2]); + KUNIT_EXPECT_EQ(test, data->vals[param->from_reg + 2], orig_val + 1); + + /* Enter cache-only and write register back to its default value */ + regcache_cache_only(map, true); + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + 2, orig_val)); + + /* Resync should write out the new value */ + regcache_cache_only(map, false); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->written[param->from_reg + i] = false; - regmap_exit(map); + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + KUNIT_EXPECT_TRUE(test, data->written[param->from_reg + 2]); + KUNIT_EXPECT_EQ(test, data->vals[param->from_reg + 2], orig_val); } static void cache_sync_readonly(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; + const struct regmap_test_param *param = test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -710,40 +1109,37 @@ static void cache_sync_readonly(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; config.writeable_reg = reg_5_false; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; /* Read all registers to fill the cache */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &val)); + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &val)); /* Change the value of all registers, readonly should fail */ get_random_bytes(&val, sizeof(val)); regcache_cache_only(map, true); for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_EXPECT_EQ(test, i != 5, regmap_write(map, i, val) == 0); + KUNIT_EXPECT_EQ(test, i != 5, regmap_write(map, param->from_reg + i, val) == 0); regcache_cache_only(map, false); /* Resync */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - data->written[i] = false; + data->written[param->from_reg + i] = false; KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); /* Did that match what we see on the device? */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_EXPECT_EQ(test, i != 5, data->written[i]); - - regmap_exit(map); + KUNIT_EXPECT_EQ(test, i != 5, data->written[param->from_reg + i]); } static void cache_sync_patch(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; + const struct regmap_test_param *param = test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -753,23 +1149,22 @@ static void cache_sync_patch(struct kunit *test) /* We need defaults so readback works */ config = test_regmap_config; - config.cache_type = t->type; config.num_reg_defaults = BLOCK_TEST_SIZE; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; /* Stash the original values */ - KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval, + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval, BLOCK_TEST_SIZE)); /* Patch a couple of values */ - patch[0].reg = 2; + patch[0].reg = param->from_reg + 2; patch[0].def = rval[2] + 1; patch[0].delay_us = 0; - patch[1].reg = 5; + patch[1].reg = param->from_reg + 5; patch[1].def = rval[5] + 1; patch[1].delay_us = 0; KUNIT_EXPECT_EQ(test, 0, regmap_register_patch(map, patch, @@ -778,33 +1173,31 @@ static void cache_sync_patch(struct kunit *test) /* Sync the cache */ regcache_mark_dirty(map); for (i = 0; i < BLOCK_TEST_SIZE; i++) - data->written[i] = false; + data->written[param->from_reg + i] = false; KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); /* The patch should be on the device but not in the cache */ for (i = 0; i < BLOCK_TEST_SIZE; i++) { - KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &val)); + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &val)); KUNIT_EXPECT_EQ(test, val, rval[i]); switch (i) { case 2: case 5: - KUNIT_EXPECT_EQ(test, true, data->written[i]); - KUNIT_EXPECT_EQ(test, data->vals[i], rval[i] + 1); + KUNIT_EXPECT_EQ(test, true, data->written[param->from_reg + i]); + KUNIT_EXPECT_EQ(test, data->vals[param->from_reg + i], rval[i] + 1); break; default: - KUNIT_EXPECT_EQ(test, false, data->written[i]); - KUNIT_EXPECT_EQ(test, data->vals[i], rval[i]); + KUNIT_EXPECT_EQ(test, false, data->written[param->from_reg + i]); + KUNIT_EXPECT_EQ(test, data->vals[param->from_reg + i], rval[i]); break; } } - - regmap_exit(map); } static void cache_drop(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; + const struct regmap_test_param *param = test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -812,41 +1205,267 @@ static void cache_drop(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; config.num_reg_defaults = BLOCK_TEST_SIZE; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; /* Ensure the data is read from the cache */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - data->read[i] = false; - KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval, + data->read[param->from_reg + i] = false; + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval, BLOCK_TEST_SIZE)); for (i = 0; i < BLOCK_TEST_SIZE; i++) { - KUNIT_EXPECT_FALSE(test, data->read[i]); - data->read[i] = false; + KUNIT_EXPECT_FALSE(test, data->read[param->from_reg + i]); + data->read[param->from_reg + i] = false; } - KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval)); + KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], rval, sizeof(rval)); /* Drop some registers */ - KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 3, 5)); + KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, param->from_reg + 3, + param->from_reg + 5)); /* Reread and check only the dropped registers hit the device. */ - KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval, + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval, BLOCK_TEST_SIZE)); for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_EXPECT_EQ(test, data->read[i], i >= 3 && i <= 5); - KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval)); + KUNIT_EXPECT_EQ(test, data->read[param->from_reg + i], i >= 3 && i <= 5); + KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], rval, sizeof(rval)); +} + +static void cache_drop_with_non_contiguous_ranges(struct kunit *test) +{ + const struct regmap_test_param *param = test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val[4][BLOCK_TEST_SIZE]; + unsigned int reg; + const int num_ranges = ARRAY_SIZE(val) * 2; + int rangeidx, i; - regmap_exit(map); + static_assert(ARRAY_SIZE(val) == 4); + + config = test_regmap_config; + config.max_register = param->from_reg + (num_ranges * BLOCK_TEST_SIZE); + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + for (i = 0; i < config.max_register + 1; i++) + data->written[i] = false; + + /* Create non-contiguous cache blocks by writing every other range */ + get_random_bytes(&val, sizeof(val)); + for (rangeidx = 0; rangeidx < num_ranges; rangeidx += 2) { + reg = param->from_reg + (rangeidx * BLOCK_TEST_SIZE); + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, reg, + &val[rangeidx / 2], + BLOCK_TEST_SIZE)); + KUNIT_EXPECT_MEMEQ(test, &data->vals[reg], + &val[rangeidx / 2], sizeof(val[rangeidx / 2])); + } + + /* Check that odd ranges weren't written */ + for (rangeidx = 1; rangeidx < num_ranges; rangeidx += 2) { + reg = param->from_reg + (rangeidx * BLOCK_TEST_SIZE); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_FALSE(test, data->written[reg + i]); + } + + /* Drop range 2 */ + reg = param->from_reg + (2 * BLOCK_TEST_SIZE); + KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, reg, reg + BLOCK_TEST_SIZE - 1)); + + /* Drop part of range 4 */ + reg = param->from_reg + (4 * BLOCK_TEST_SIZE); + KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, reg + 3, reg + 5)); + + /* Mark dirty and reset mock registers to 0 */ + regcache_mark_dirty(map); + for (i = 0; i < config.max_register + 1; i++) { + data->vals[i] = 0; + data->written[i] = false; + } + + /* The registers that were dropped from range 4 should now remain at 0 */ + val[4 / 2][3] = 0; + val[4 / 2][4] = 0; + val[4 / 2][5] = 0; + + /* Sync and check that the expected register ranges were written */ + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + + /* Check that odd ranges weren't written */ + for (rangeidx = 1; rangeidx < num_ranges; rangeidx += 2) { + reg = param->from_reg + (rangeidx * BLOCK_TEST_SIZE); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_FALSE(test, data->written[reg + i]); + } + + /* Check that even ranges (except 2 and 4) were written */ + for (rangeidx = 0; rangeidx < num_ranges; rangeidx += 2) { + if ((rangeidx == 2) || (rangeidx == 4)) + continue; + + reg = param->from_reg + (rangeidx * BLOCK_TEST_SIZE); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_TRUE(test, data->written[reg + i]); + + KUNIT_EXPECT_MEMEQ(test, &data->vals[reg], + &val[rangeidx / 2], sizeof(val[rangeidx / 2])); + } + + /* Check that range 2 wasn't written */ + reg = param->from_reg + (2 * BLOCK_TEST_SIZE); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_FALSE(test, data->written[reg + i]); + + /* Check that range 4 was partially written */ + reg = param->from_reg + (4 * BLOCK_TEST_SIZE); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, data->written[reg + i], i < 3 || i > 5); + + KUNIT_EXPECT_MEMEQ(test, &data->vals[reg], &val[4 / 2], sizeof(val[4 / 2])); + + /* Nothing before param->from_reg should have been written */ + for (i = 0; i < param->from_reg; i++) + KUNIT_EXPECT_FALSE(test, data->written[i]); +} + +static void cache_drop_all_and_sync_marked_dirty(struct kunit *test) +{ + const struct regmap_test_param *param = test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int rval[BLOCK_TEST_SIZE]; + int i; + + config = test_regmap_config; + config.num_reg_defaults = BLOCK_TEST_SIZE; + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + /* Ensure the data is read from the cache */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->read[param->from_reg + i] = false; + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval, + BLOCK_TEST_SIZE)); + KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], rval, sizeof(rval)); + + /* Change all values in cache from defaults */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + i, rval[i] + 1)); + + /* Drop all registers */ + KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 0, config.max_register)); + + /* Mark dirty and cache sync should not write anything. */ + regcache_mark_dirty(map); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->written[param->from_reg + i] = false; + + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + for (i = 0; i <= config.max_register; i++) + KUNIT_EXPECT_FALSE(test, data->written[i]); +} + +static void cache_drop_all_and_sync_no_defaults(struct kunit *test) +{ + const struct regmap_test_param *param = test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int rval[BLOCK_TEST_SIZE]; + int i; + + config = test_regmap_config; + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + /* Ensure the data is read from the cache */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->read[param->from_reg + i] = false; + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval, + BLOCK_TEST_SIZE)); + KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], rval, sizeof(rval)); + + /* Change all values in cache */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + i, rval[i] + 1)); + + /* Drop all registers */ + KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 0, config.max_register)); + + /* + * Sync cache without marking it dirty. All registers were dropped + * so the cache should not have any entries to write out. + */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->written[param->from_reg + i] = false; + + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + for (i = 0; i <= config.max_register; i++) + KUNIT_EXPECT_FALSE(test, data->written[i]); +} + +static void cache_drop_all_and_sync_has_defaults(struct kunit *test) +{ + const struct regmap_test_param *param = test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int rval[BLOCK_TEST_SIZE]; + int i; + + config = test_regmap_config; + config.num_reg_defaults = BLOCK_TEST_SIZE; + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + /* Ensure the data is read from the cache */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->read[param->from_reg + i] = false; + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval, + BLOCK_TEST_SIZE)); + KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], rval, sizeof(rval)); + + /* Change all values in cache from defaults */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + i, rval[i] + 1)); + + /* Drop all registers */ + KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 0, config.max_register)); + + /* + * Sync cache without marking it dirty. All registers were dropped + * so the cache should not have any entries to write out. + */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->written[param->from_reg + i] = false; + + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + for (i = 0; i <= config.max_register; i++) + KUNIT_EXPECT_FALSE(test, data->written[i]); } static void cache_present(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; + const struct regmap_test_param *param = test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -854,39 +1473,77 @@ static void cache_present(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; for (i = 0; i < BLOCK_TEST_SIZE; i++) - data->read[i] = false; + data->read[param->from_reg + i] = false; /* No defaults so no registers cached. */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, i)); + KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, param->from_reg + i)); /* We didn't trigger any reads */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_ASSERT_FALSE(test, data->read[i]); + KUNIT_ASSERT_FALSE(test, data->read[param->from_reg + i]); /* Fill the cache */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &val)); + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &val)); /* Now everything should be cached */ for (i = 0; i < BLOCK_TEST_SIZE; i++) - KUNIT_ASSERT_TRUE(test, regcache_reg_cached(map, i)); + KUNIT_ASSERT_TRUE(test, regcache_reg_cached(map, param->from_reg + i)); +} - regmap_exit(map); +static void cache_write_zero(struct kunit *test) +{ + const struct regmap_test_param *param = test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val; + int i; + + config = test_regmap_config; + + map = gen_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->read[param->from_reg + i] = false; + + /* No defaults so no registers cached. */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, param->from_reg + i)); + + /* We didn't trigger any reads */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_ASSERT_FALSE(test, data->read[param->from_reg + i]); + + /* Write a zero value */ + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 1, 0)); + + /* Read that zero value back */ + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 1, &val)); + KUNIT_EXPECT_EQ(test, 0, val); + + /* From the cache? */ + KUNIT_ASSERT_TRUE(test, regcache_reg_cached(map, 1)); + + /* Try to throw it away */ + KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 1, 1)); + KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, 1)); } /* Check that caching the window register works with sync */ static void cache_range_window_reg(struct kunit *test) { - struct regcache_types *t = (struct regcache_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -894,13 +1551,12 @@ static void cache_range_window_reg(struct kunit *test) int i; config = test_regmap_config; - config.cache_type = t->type; config.volatile_reg = test_range_window_volatile; config.ranges = &test_range; config.num_ranges = 1; config.max_register = test_range.range_max; - map = gen_regmap(&config, &data); + map = gen_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -936,41 +1592,29 @@ static void cache_range_window_reg(struct kunit *test) KUNIT_ASSERT_EQ(test, val, 2); } -struct raw_test_types { - const char *name; - - enum regcache_type cache_type; - enum regmap_endian val_endian; -}; - -static void raw_to_desc(const struct raw_test_types *t, char *desc) -{ - strcpy(desc, t->name); -} - -static const struct raw_test_types raw_types_list[] = { - { "none-little", REGCACHE_NONE, REGMAP_ENDIAN_LITTLE }, - { "none-big", REGCACHE_NONE, REGMAP_ENDIAN_BIG }, - { "flat-little", REGCACHE_FLAT, REGMAP_ENDIAN_LITTLE }, - { "flat-big", REGCACHE_FLAT, REGMAP_ENDIAN_BIG }, - { "rbtree-little", REGCACHE_RBTREE, REGMAP_ENDIAN_LITTLE }, - { "rbtree-big", REGCACHE_RBTREE, REGMAP_ENDIAN_BIG }, - { "maple-little", REGCACHE_MAPLE, REGMAP_ENDIAN_LITTLE }, - { "maple-big", REGCACHE_MAPLE, REGMAP_ENDIAN_BIG }, +static const struct regmap_test_param raw_types_list[] = { + { .cache = REGCACHE_NONE, .val_endian = REGMAP_ENDIAN_LITTLE }, + { .cache = REGCACHE_NONE, .val_endian = REGMAP_ENDIAN_BIG }, + { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE }, + { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG }, + { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE }, + { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG }, + { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE }, + { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_BIG }, }; -KUNIT_ARRAY_PARAM(raw_test_types, raw_types_list, raw_to_desc); +KUNIT_ARRAY_PARAM(raw_test_types, raw_types_list, param_to_desc); -static const struct raw_test_types raw_cache_types_list[] = { - { "flat-little", REGCACHE_FLAT, REGMAP_ENDIAN_LITTLE }, - { "flat-big", REGCACHE_FLAT, REGMAP_ENDIAN_BIG }, - { "rbtree-little", REGCACHE_RBTREE, REGMAP_ENDIAN_LITTLE }, - { "rbtree-big", REGCACHE_RBTREE, REGMAP_ENDIAN_BIG }, - { "maple-little", REGCACHE_MAPLE, REGMAP_ENDIAN_LITTLE }, - { "maple-big", REGCACHE_MAPLE, REGMAP_ENDIAN_BIG }, +static const struct regmap_test_param raw_cache_types_list[] = { + { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE }, + { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG }, + { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE }, + { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG }, + { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE }, + { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_BIG }, }; -KUNIT_ARRAY_PARAM(raw_test_cache_types, raw_cache_types_list, raw_to_desc); +KUNIT_ARRAY_PARAM(raw_test_cache_types, raw_cache_types_list, param_to_desc); static const struct regmap_config raw_regmap_config = { .max_register = BLOCK_TEST_SIZE, @@ -980,21 +1624,24 @@ static const struct regmap_config raw_regmap_config = { .val_bits = 16, }; -static struct regmap *gen_raw_regmap(struct regmap_config *config, - struct raw_test_types *test_type, +static struct regmap *gen_raw_regmap(struct kunit *test, + struct regmap_config *config, struct regmap_ram_data **data) { + struct regmap_test_priv *priv = test->priv; + const struct regmap_test_param *param = test->param_value; u16 *buf; - struct regmap *ret; - size_t size = (config->max_register + 1) * config->reg_bits / 8; - int i; + struct regmap *ret = ERR_PTR(-ENOMEM); + int i, error; struct reg_default *defaults; + size_t size; - config->cache_type = test_type->cache_type; - config->val_format_endian = test_type->val_endian; + config->cache_type = param->cache; + config->val_format_endian = param->val_endian; config->disable_locking = config->cache_type == REGCACHE_RBTREE || config->cache_type == REGCACHE_MAPLE; + size = array_size(config->max_register + 1, BITS_TO_BYTES(config->reg_bits)); buf = kmalloc(size, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); @@ -1003,20 +1650,21 @@ static struct regmap *gen_raw_regmap(struct regmap_config *config, *data = kzalloc(sizeof(**data), GFP_KERNEL); if (!(*data)) - return ERR_PTR(-ENOMEM); + goto out_free; (*data)->vals = (void *)buf; config->num_reg_defaults = config->max_register + 1; - defaults = kcalloc(config->num_reg_defaults, - sizeof(struct reg_default), - GFP_KERNEL); + defaults = kunit_kcalloc(test, + config->num_reg_defaults, + sizeof(struct reg_default), + GFP_KERNEL); if (!defaults) - return ERR_PTR(-ENOMEM); + goto out_free; config->reg_defaults = defaults; for (i = 0; i < config->num_reg_defaults; i++) { defaults[i].reg = i; - switch (test_type->val_endian) { + switch (param->val_endian) { case REGMAP_ENDIAN_LITTLE: defaults[i].def = le16_to_cpu(buf[i]); break; @@ -1024,7 +1672,8 @@ static struct regmap *gen_raw_regmap(struct regmap_config *config, defaults[i].def = be16_to_cpu(buf[i]); break; default: - return ERR_PTR(-EINVAL); + ret = ERR_PTR(-EINVAL); + goto out_free; } } @@ -1035,18 +1684,26 @@ static struct regmap *gen_raw_regmap(struct regmap_config *config, if (config->cache_type == REGCACHE_NONE) config->num_reg_defaults = 0; - ret = regmap_init_raw_ram(config, *data); - if (IS_ERR(ret)) { - kfree(buf); - kfree(*data); - } + ret = regmap_init_raw_ram(priv->dev, config, *data); + if (IS_ERR(ret)) + goto out_free; + + /* This calls regmap_exit() on failure, which frees buf and *data */ + error = kunit_add_action_or_reset(test, regmap_exit_action, ret); + if (error) + ret = ERR_PTR(error); + + return ret; + +out_free: + kfree(buf); + kfree(*data); return ret; } static void raw_read_defaults_single(struct kunit *test) { - struct raw_test_types *t = (struct raw_test_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -1055,7 +1712,7 @@ static void raw_read_defaults_single(struct kunit *test) config = raw_regmap_config; - map = gen_raw_regmap(&config, t, &data); + map = gen_raw_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -1065,13 +1722,10 @@ static void raw_read_defaults_single(struct kunit *test) KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval)); KUNIT_EXPECT_EQ(test, config.reg_defaults[i].def, rval); } - - regmap_exit(map); } static void raw_read_defaults(struct kunit *test) { - struct raw_test_types *t = (struct raw_test_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -1082,35 +1736,31 @@ static void raw_read_defaults(struct kunit *test) config = raw_regmap_config; - map = gen_raw_regmap(&config, t, &data); + map = gen_raw_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; - val_len = sizeof(*rval) * (config.max_register + 1); - rval = kmalloc(val_len, GFP_KERNEL); + val_len = array_size(sizeof(*rval), config.max_register + 1); + rval = kunit_kmalloc(test, val_len, GFP_KERNEL); KUNIT_ASSERT_TRUE(test, rval != NULL); if (!rval) return; - + /* Check that we can read the defaults via the API */ KUNIT_EXPECT_EQ(test, 0, regmap_raw_read(map, 0, rval, val_len)); for (i = 0; i < config.max_register + 1; i++) { def = config.reg_defaults[i].def; if (config.val_format_endian == REGMAP_ENDIAN_BIG) { - KUNIT_EXPECT_EQ(test, def, be16_to_cpu(rval[i])); + KUNIT_EXPECT_EQ(test, def, be16_to_cpu((__force __be16)rval[i])); } else { - KUNIT_EXPECT_EQ(test, def, le16_to_cpu(rval[i])); + KUNIT_EXPECT_EQ(test, def, le16_to_cpu((__force __le16)rval[i])); } } - - kfree(rval); - regmap_exit(map); } static void raw_write_read_single(struct kunit *test) { - struct raw_test_types *t = (struct raw_test_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -1119,7 +1769,7 @@ static void raw_write_read_single(struct kunit *test) config = raw_regmap_config; - map = gen_raw_regmap(&config, t, &data); + map = gen_raw_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -1130,13 +1780,10 @@ static void raw_write_read_single(struct kunit *test) KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val)); KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval)); KUNIT_EXPECT_EQ(test, val, rval); - - regmap_exit(map); } static void raw_write(struct kunit *test) { - struct raw_test_types *t = (struct raw_test_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; @@ -1147,7 +1794,7 @@ static void raw_write(struct kunit *test) config = raw_regmap_config; - map = gen_raw_regmap(&config, t, &data); + map = gen_raw_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -1168,10 +1815,10 @@ static void raw_write(struct kunit *test) case 3: if (config.val_format_endian == REGMAP_ENDIAN_BIG) { KUNIT_EXPECT_EQ(test, rval, - be16_to_cpu(val[i % 2])); + be16_to_cpu((__force __be16)val[i % 2])); } else { KUNIT_EXPECT_EQ(test, rval, - le16_to_cpu(val[i % 2])); + le16_to_cpu((__force __le16)val[i % 2])); } break; default: @@ -1182,8 +1829,6 @@ static void raw_write(struct kunit *test) /* The values should appear in the "hardware" */ KUNIT_EXPECT_MEMEQ(test, &hw_buf[2], val, sizeof(val)); - - regmap_exit(map); } static bool reg_zero(struct device *dev, unsigned int reg) @@ -1198,11 +1843,11 @@ static bool ram_reg_zero(struct regmap_ram_data *data, unsigned int reg) static void raw_noinc_write(struct kunit *test) { - struct raw_test_types *t = (struct raw_test_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; - unsigned int val, val_test, val_last; + unsigned int val; + u16 val_test, val_last; u16 val_array[BLOCK_TEST_SIZE]; config = raw_regmap_config; @@ -1210,7 +1855,7 @@ static void raw_noinc_write(struct kunit *test) config.writeable_noinc_reg = reg_zero; config.readable_noinc_reg = reg_zero; - map = gen_raw_regmap(&config, t, &data); + map = gen_raw_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; @@ -1241,41 +1886,34 @@ static void raw_noinc_write(struct kunit *test) /* Make sure we didn't touch the register after the noinc register */ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 1, &val)); KUNIT_ASSERT_EQ(test, val_test, val); - - regmap_exit(map); } static void raw_sync(struct kunit *test) { - struct raw_test_types *t = (struct raw_test_types *)test->param_value; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; - u16 val[2]; + u16 val[3]; u16 *hw_buf; unsigned int rval; int i; config = raw_regmap_config; - map = gen_raw_regmap(&config, t, &data); + map = gen_raw_regmap(test, &config, &data); KUNIT_ASSERT_FALSE(test, IS_ERR(map)); if (IS_ERR(map)) return; hw_buf = (u16 *)data->vals; - get_random_bytes(&val, sizeof(val)); + get_changed_bytes(&hw_buf[2], &val[0], sizeof(val)); /* Do a regular write and a raw write in cache only mode */ regcache_cache_only(map, true); - KUNIT_EXPECT_EQ(test, 0, regmap_raw_write(map, 2, val, sizeof(val))); - if (config.val_format_endian == REGMAP_ENDIAN_BIG) - KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 6, - be16_to_cpu(val[0]))); - else - KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 6, - le16_to_cpu(val[0]))); + KUNIT_EXPECT_EQ(test, 0, regmap_raw_write(map, 2, val, + sizeof(u16) * 2)); + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 4, val[2])); /* We should read back the new values, and defaults for the rest */ for (i = 0; i < config.max_register + 1; i++) { @@ -1284,24 +1922,34 @@ static void raw_sync(struct kunit *test) switch (i) { case 2: case 3: - case 6: if (config.val_format_endian == REGMAP_ENDIAN_BIG) { KUNIT_EXPECT_EQ(test, rval, - be16_to_cpu(val[i % 2])); + be16_to_cpu((__force __be16)val[i - 2])); } else { KUNIT_EXPECT_EQ(test, rval, - le16_to_cpu(val[i % 2])); + le16_to_cpu((__force __le16)val[i - 2])); } break; + case 4: + KUNIT_EXPECT_EQ(test, rval, val[i - 2]); + break; default: KUNIT_EXPECT_EQ(test, config.reg_defaults[i].def, rval); break; } } - + + /* + * The value written via _write() was translated by the core, + * translate the original copy for comparison purposes. + */ + if (config.val_format_endian == REGMAP_ENDIAN_BIG) + val[2] = cpu_to_be16(val[2]); + else + val[2] = cpu_to_le16(val[2]); + /* The values should not appear in the "hardware" */ - KUNIT_EXPECT_MEMNEQ(test, &hw_buf[2], val, sizeof(val)); - KUNIT_EXPECT_MEMNEQ(test, &hw_buf[6], val, sizeof(u16)); + KUNIT_EXPECT_MEMNEQ(test, &hw_buf[2], &val[0], sizeof(val)); for (i = 0; i < config.max_register + 1; i++) data->written[i] = false; @@ -1312,16 +1960,79 @@ static void raw_sync(struct kunit *test) KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); /* The values should now appear in the "hardware" */ - KUNIT_EXPECT_MEMEQ(test, &hw_buf[2], val, sizeof(val)); - KUNIT_EXPECT_MEMEQ(test, &hw_buf[6], val, sizeof(u16)); + KUNIT_EXPECT_MEMEQ(test, &hw_buf[2], &val[0], sizeof(val)); +} + +static void raw_ranges(struct kunit *test) +{ + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val; + int i; + + config = raw_regmap_config; + config.volatile_reg = test_range_all_volatile; + config.ranges = &test_range; + config.num_ranges = 1; + config.max_register = test_range.range_max; + + map = gen_raw_regmap(test, &config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + /* Reset the page to a non-zero value to trigger a change */ + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, test_range.selector_reg, + test_range.range_max)); + + /* Check we set the page and use the window for writes */ + data->written[test_range.selector_reg] = false; + data->written[test_range.window_start] = false; + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, test_range.range_min, 0)); + KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]); + KUNIT_EXPECT_TRUE(test, data->written[test_range.window_start]); + + data->written[test_range.selector_reg] = false; + data->written[test_range.window_start] = false; + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, + test_range.range_min + + test_range.window_len, + 0)); + KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]); + KUNIT_EXPECT_TRUE(test, data->written[test_range.window_start]); + + /* Same for reads */ + data->written[test_range.selector_reg] = false; + data->read[test_range.window_start] = false; + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, test_range.range_min, &val)); + KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]); + KUNIT_EXPECT_TRUE(test, data->read[test_range.window_start]); + + data->written[test_range.selector_reg] = false; + data->read[test_range.window_start] = false; + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, + test_range.range_min + + test_range.window_len, + &val)); + KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]); + KUNIT_EXPECT_TRUE(test, data->read[test_range.window_start]); - regmap_exit(map); + /* No physical access triggered in the virtual range */ + for (i = test_range.range_min; i < test_range.range_max; i++) { + KUNIT_EXPECT_FALSE(test, data->read[i]); + KUNIT_EXPECT_FALSE(test, data->written[i]); + } } static struct kunit_case regmap_test_cases[] = { KUNIT_CASE_PARAM(basic_read_write, regcache_types_gen_params), + KUNIT_CASE_PARAM(read_bypassed, real_cache_types_gen_params), + KUNIT_CASE_PARAM(read_bypassed_volatile, real_cache_types_gen_params), KUNIT_CASE_PARAM(bulk_write, regcache_types_gen_params), KUNIT_CASE_PARAM(bulk_read, regcache_types_gen_params), + KUNIT_CASE_PARAM(multi_write, regcache_types_gen_params), + KUNIT_CASE_PARAM(multi_read, regcache_types_gen_params), KUNIT_CASE_PARAM(write_readonly, regcache_types_gen_params), KUNIT_CASE_PARAM(read_writeonly, regcache_types_gen_params), KUNIT_CASE_PARAM(reg_defaults, regcache_types_gen_params), @@ -1331,13 +2042,20 @@ static struct kunit_case regmap_test_cases[] = { KUNIT_CASE_PARAM(basic_ranges, regcache_types_gen_params), KUNIT_CASE_PARAM(stress_insert, regcache_types_gen_params), KUNIT_CASE_PARAM(cache_bypass, real_cache_types_gen_params), - KUNIT_CASE_PARAM(cache_sync, real_cache_types_gen_params), - KUNIT_CASE_PARAM(cache_sync_defaults, real_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_sync_marked_dirty, real_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_sync_after_cache_only, real_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_sync_defaults_marked_dirty, real_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_sync_default_after_cache_only, real_cache_types_gen_params), KUNIT_CASE_PARAM(cache_sync_readonly, real_cache_types_gen_params), KUNIT_CASE_PARAM(cache_sync_patch, real_cache_types_gen_params), KUNIT_CASE_PARAM(cache_drop, sparse_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_drop_with_non_contiguous_ranges, sparse_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_drop_all_and_sync_marked_dirty, sparse_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_drop_all_and_sync_no_defaults, sparse_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_drop_all_and_sync_has_defaults, sparse_cache_types_gen_params), KUNIT_CASE_PARAM(cache_present, sparse_cache_types_gen_params), - KUNIT_CASE_PARAM(cache_range_window_reg, real_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_write_zero, sparse_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_range_window_reg, real_cache_types_only_gen_params), KUNIT_CASE_PARAM(raw_read_defaults_single, raw_test_types_gen_params), KUNIT_CASE_PARAM(raw_read_defaults, raw_test_types_gen_params), @@ -1345,13 +2063,47 @@ static struct kunit_case regmap_test_cases[] = { KUNIT_CASE_PARAM(raw_write, raw_test_types_gen_params), KUNIT_CASE_PARAM(raw_noinc_write, raw_test_types_gen_params), KUNIT_CASE_PARAM(raw_sync, raw_test_cache_types_gen_params), + KUNIT_CASE_PARAM(raw_ranges, raw_test_cache_types_gen_params), {} }; +static int regmap_test_init(struct kunit *test) +{ + struct regmap_test_priv *priv; + struct device *dev; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + test->priv = priv; + + dev = kunit_device_register(test, "regmap_test"); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + priv->dev = get_device(dev); + dev_set_drvdata(dev, test); + + return 0; +} + +static void regmap_test_exit(struct kunit *test) +{ + struct regmap_test_priv *priv = test->priv; + + /* Destroy the dummy struct device */ + if (priv && priv->dev) + put_device(priv->dev); +} + static struct kunit_suite regmap_test_suite = { .name = "regmap", + .init = regmap_test_init, + .exit = regmap_test_exit, .test_cases = regmap_test_cases, }; kunit_test_suite(regmap_test_suite); +MODULE_DESCRIPTION("Regmap KUnit tests"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-mdio.c b/drivers/base/regmap/regmap-mdio.c index 6aa6a2409478..9573bf3b52f4 100644 --- a/drivers/base/regmap/regmap-mdio.c +++ b/drivers/base/regmap/regmap-mdio.c @@ -117,5 +117,5 @@ struct regmap *__devm_regmap_init_mdio(struct mdio_device *mdio_dev, EXPORT_SYMBOL_GPL(__devm_regmap_init_mdio); MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>"); -MODULE_DESCRIPTION("Regmap MDIO Module"); +MODULE_DESCRIPTION("regmap MDIO Module"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-ram.c b/drivers/base/regmap/regmap-ram.c index 192d6b131dff..4e5b4518ce4d 100644 --- a/drivers/base/regmap/regmap-ram.c +++ b/drivers/base/regmap/regmap-ram.c @@ -53,7 +53,8 @@ static const struct regmap_bus regmap_ram = { .free_context = regmap_ram_free_context, }; -struct regmap *__regmap_init_ram(const struct regmap_config *config, +struct regmap *__regmap_init_ram(struct device *dev, + const struct regmap_config *config, struct regmap_ram_data *data, struct lock_class_key *lock_key, const char *lock_name) @@ -75,11 +76,12 @@ struct regmap *__regmap_init_ram(const struct regmap_config *config, if (!data->written) return ERR_PTR(-ENOMEM); - map = __regmap_init(NULL, ®map_ram, data, config, + map = __regmap_init(dev, ®map_ram, data, config, lock_key, lock_name); return map; } EXPORT_SYMBOL_GPL(__regmap_init_ram); +MODULE_DESCRIPTION("Register map access API - Memory region"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-raw-ram.c b/drivers/base/regmap/regmap-raw-ram.c index 93ae07b503fd..76c98814fb8a 100644 --- a/drivers/base/regmap/regmap-raw-ram.c +++ b/drivers/base/regmap/regmap-raw-ram.c @@ -107,7 +107,8 @@ static const struct regmap_bus regmap_raw_ram = { .free_context = regmap_raw_ram_free_context, }; -struct regmap *__regmap_init_raw_ram(const struct regmap_config *config, +struct regmap *__regmap_init_raw_ram(struct device *dev, + const struct regmap_config *config, struct regmap_ram_data *data, struct lock_class_key *lock_key, const char *lock_name) @@ -134,11 +135,12 @@ struct regmap *__regmap_init_raw_ram(const struct regmap_config *config, data->reg_endian = config->reg_format_endian; - map = __regmap_init(NULL, ®map_raw_ram, data, config, + map = __regmap_init(dev, ®map_raw_ram, data, config, lock_key, lock_name); return map; } EXPORT_SYMBOL_GPL(__regmap_init_raw_ram); +MODULE_DESCRIPTION("Register map access API - Memory region with raw access"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-sccb.c b/drivers/base/regmap/regmap-sccb.c index 986af26d88c2..12bbbb03e5f2 100644 --- a/drivers/base/regmap/regmap-sccb.c +++ b/drivers/base/regmap/regmap-sccb.c @@ -125,4 +125,5 @@ struct regmap *__devm_regmap_init_sccb(struct i2c_client *i2c, } EXPORT_SYMBOL_GPL(__devm_regmap_init_sccb); +MODULE_DESCRIPTION("Register map access API - SCCB support"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-sdw-mbq.c b/drivers/base/regmap/regmap-sdw-mbq.c index 388c3a087bd9..86644bbd0710 100644 --- a/drivers/base/regmap/regmap-sdw-mbq.c +++ b/drivers/base/regmap/regmap-sdw-mbq.c @@ -1,45 +1,187 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright(c) 2020 Intel Corporation. +#include <linux/bits.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/errno.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_registers.h> +#include <sound/sdca_function.h> #include "internal.h" +struct regmap_mbq_context { + struct device *dev; + + struct regmap_sdw_mbq_cfg cfg; + + int val_size; + bool (*readable_reg)(struct device *dev, unsigned int reg); +}; + +static int regmap_sdw_mbq_size(struct regmap_mbq_context *ctx, unsigned int reg) +{ + int size = ctx->val_size; + + if (ctx->cfg.mbq_size) { + size = ctx->cfg.mbq_size(ctx->dev, reg); + if (!size || size > ctx->val_size) + return -EINVAL; + } + + return size; +} + +static bool regmap_sdw_mbq_deferrable(struct regmap_mbq_context *ctx, unsigned int reg) +{ + if (ctx->cfg.deferrable) + return ctx->cfg.deferrable(ctx->dev, reg); + + return false; +} + +static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg, + struct regmap_mbq_context *ctx) +{ + struct device *dev = &slave->dev; + int val, ret = 0; + + dev_dbg(dev, "Deferring transaction for 0x%x\n", reg); + + reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(reg), 0, + SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0); + + if (ctx->readable_reg(dev, reg)) { + ret = read_poll_timeout(sdw_read_no_pm, val, + val < 0 || !(val & SDCA_CTL_ENTITY_0_FUNCTION_BUSY), + ctx->cfg.timeout_us, ctx->cfg.retry_us, + false, slave, reg); + if (val < 0) + return val; + if (ret) + dev_err(dev, "Function busy timed out 0x%x: %d\n", reg, val); + } else { + fsleep(ctx->cfg.timeout_us); + } + + return ret; +} + +static int regmap_sdw_mbq_write_impl(struct sdw_slave *slave, + unsigned int reg, unsigned int val, + int mbq_size, bool deferrable) +{ + int shift = mbq_size * BITS_PER_BYTE; + int ret; + + while (--mbq_size > 0) { + shift -= BITS_PER_BYTE; + + ret = sdw_write_no_pm(slave, SDW_SDCA_MBQ_CTL(reg), + (val >> shift) & 0xff); + if (ret < 0) + return ret; + } + + ret = sdw_write_no_pm(slave, reg, val & 0xff); + if (deferrable && ret == -ENODATA) + return -EAGAIN; + + return ret; +} + static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val) { - struct device *dev = context; + struct regmap_mbq_context *ctx = context; + struct device *dev = ctx->dev; struct sdw_slave *slave = dev_to_sdw_dev(dev); + bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg); + int mbq_size = regmap_sdw_mbq_size(ctx, reg); int ret; - ret = sdw_write_no_pm(slave, SDW_SDCA_MBQ_CTL(reg), (val >> 8) & 0xff); - if (ret < 0) - return ret; + if (mbq_size < 0) + return mbq_size; + + /* + * Technically the spec does allow a device to set itself to busy for + * internal reasons, but since it doesn't provide any information on + * how to handle timeouts in that case, for now the code will only + * process a single wait/timeout on function busy and a single retry + * of the transaction. + */ + ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, deferrable); + if (ret == -EAGAIN) { + ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx); + if (ret) + return ret; + + ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, false); + } + + return ret; +} + +static int regmap_sdw_mbq_read_impl(struct sdw_slave *slave, + unsigned int reg, unsigned int *val, + int mbq_size, bool deferrable) +{ + int shift = BITS_PER_BYTE; + int read; + + read = sdw_read_no_pm(slave, reg); + if (read < 0) { + if (deferrable && read == -ENODATA) + return -EAGAIN; + + return read; + } + + *val = read; + + while (--mbq_size > 0) { + read = sdw_read_no_pm(slave, SDW_SDCA_MBQ_CTL(reg)); + if (read < 0) + return read; + + *val |= read << shift; + shift += BITS_PER_BYTE; + } - return sdw_write_no_pm(slave, reg, val & 0xff); + return 0; } static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val) { - struct device *dev = context; + struct regmap_mbq_context *ctx = context; + struct device *dev = ctx->dev; struct sdw_slave *slave = dev_to_sdw_dev(dev); - int read0; - int read1; + bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg); + int mbq_size = regmap_sdw_mbq_size(ctx, reg); + int ret; - read0 = sdw_read_no_pm(slave, reg); - if (read0 < 0) - return read0; + if (mbq_size < 0) + return mbq_size; - read1 = sdw_read_no_pm(slave, SDW_SDCA_MBQ_CTL(reg)); - if (read1 < 0) - return read1; + /* + * Technically the spec does allow a device to set itself to busy for + * internal reasons, but since it doesn't provide any information on + * how to handle timeouts in that case, for now the code will only + * process a single wait/timeout on function busy and a single retry + * of the transaction. + */ + ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, deferrable); + if (ret == -EAGAIN) { + ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx); + if (ret) + return ret; - *val = (read1 << 8) | read0; + ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, false); + } - return 0; + return ret; } static const struct regmap_bus regmap_sdw_mbq = { @@ -51,8 +193,7 @@ static const struct regmap_bus regmap_sdw_mbq = { static int regmap_sdw_mbq_config_check(const struct regmap_config *config) { - /* MBQ-based controls are only 16-bits for now */ - if (config->val_bits != 16) + if (config->val_bits > (sizeof(unsigned int) * BITS_PER_BYTE)) return -ENOTSUPP; /* Registers are 32 bits wide */ @@ -65,37 +206,71 @@ static int regmap_sdw_mbq_config_check(const struct regmap_config *config) return 0; } +static struct regmap_mbq_context * +regmap_sdw_mbq_gen_context(struct device *dev, + const struct regmap_config *config, + const struct regmap_sdw_mbq_cfg *mbq_config) +{ + struct regmap_mbq_context *ctx; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->dev = dev; + + if (mbq_config) + ctx->cfg = *mbq_config; + + ctx->val_size = config->val_bits / BITS_PER_BYTE; + ctx->readable_reg = config->readable_reg; + + return ctx; +} + struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw, const struct regmap_config *config, + const struct regmap_sdw_mbq_cfg *mbq_config, struct lock_class_key *lock_key, const char *lock_name) { + struct regmap_mbq_context *ctx; int ret; ret = regmap_sdw_mbq_config_check(config); if (ret) return ERR_PTR(ret); - return __regmap_init(&sdw->dev, ®map_sdw_mbq, - &sdw->dev, config, lock_key, lock_name); + ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return __regmap_init(&sdw->dev, ®map_sdw_mbq, ctx, + config, lock_key, lock_name); } EXPORT_SYMBOL_GPL(__regmap_init_sdw_mbq); struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw, const struct regmap_config *config, + const struct regmap_sdw_mbq_cfg *mbq_config, struct lock_class_key *lock_key, const char *lock_name) { + struct regmap_mbq_context *ctx; int ret; ret = regmap_sdw_mbq_config_check(config); if (ret) return ERR_PTR(ret); - return __devm_regmap_init(&sdw->dev, ®map_sdw_mbq, - &sdw->dev, config, lock_key, lock_name); + ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return __devm_regmap_init(&sdw->dev, ®map_sdw_mbq, ctx, + config, lock_key, lock_name); } EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq); -MODULE_DESCRIPTION("Regmap SoundWire MBQ Module"); +MODULE_DESCRIPTION("regmap SoundWire MBQ Module"); MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap-sdw.c b/drivers/base/regmap/regmap-sdw.c index 159c0b740b00..ea631ac7c7ec 100644 --- a/drivers/base/regmap/regmap-sdw.c +++ b/drivers/base/regmap/regmap-sdw.c @@ -98,5 +98,5 @@ struct regmap *__devm_regmap_init_sdw(struct sdw_slave *sdw, } EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw); -MODULE_DESCRIPTION("Regmap SoundWire Module"); +MODULE_DESCRIPTION("regmap SoundWire Module"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-slimbus.c b/drivers/base/regmap/regmap-slimbus.c index 8075db788b39..54eb7d227cf4 100644 --- a/drivers/base/regmap/regmap-slimbus.c +++ b/drivers/base/regmap/regmap-slimbus.c @@ -68,4 +68,5 @@ struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus, } EXPORT_SYMBOL_GPL(__devm_regmap_init_slimbus); +MODULE_DESCRIPTION("Register map access API - SLIMbus support"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-spi-avmm.c b/drivers/base/regmap/regmap-spi-avmm.c index 4c2b94b3e30b..d86a06cadcdb 100644 --- a/drivers/base/regmap/regmap-spi-avmm.c +++ b/drivers/base/regmap/regmap-spi-avmm.c @@ -710,4 +710,5 @@ struct regmap *__devm_regmap_init_spi_avmm(struct spi_device *spi, } EXPORT_SYMBOL_GPL(__devm_regmap_init_spi_avmm); +MODULE_DESCRIPTION("Register map access API - SPI AVMM support"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 37ab23a9d034..14b1d88997cb 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -122,8 +122,7 @@ static const struct regmap_bus *regmap_get_spi_bus(struct spi_device *spi, return ERR_PTR(-ENOMEM); max_msg_size = spi_max_message_size(spi); - reg_reserve_size = config->reg_bits / BITS_PER_BYTE - + config->pad_bits / BITS_PER_BYTE; + reg_reserve_size = (config->reg_bits + config->pad_bits) / BITS_PER_BYTE; if (max_size + reg_reserve_size > max_msg_size) max_size -= reg_reserve_size; @@ -165,4 +164,5 @@ struct regmap *__devm_regmap_init_spi(struct spi_device *spi, } EXPORT_SYMBOL_GPL(__devm_regmap_init_spi); +MODULE_DESCRIPTION("regmap SPI Module"); MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap-spmi.c b/drivers/base/regmap/regmap-spmi.c index cdf12d2aa3a1..347bfe9544ce 100644 --- a/drivers/base/regmap/regmap-spmi.c +++ b/drivers/base/regmap/regmap-spmi.c @@ -222,4 +222,5 @@ struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev, } EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext); +MODULE_DESCRIPTION("Register map access API - SPMI support"); MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap-w1.c b/drivers/base/regmap/regmap-w1.c index 3a8b402db852..29fd24f9c7ed 100644 --- a/drivers/base/regmap/regmap-w1.c +++ b/drivers/base/regmap/regmap-w1.c @@ -234,4 +234,5 @@ struct regmap *__devm_regmap_init_w1(struct device *w1_dev, } EXPORT_SYMBOL_GPL(__devm_regmap_init_w1); +MODULE_DESCRIPTION("Register map access API - W1 (1-Wire) support"); MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6db77d8e45f9..f2843f814675 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -17,7 +17,7 @@ #include <linux/delay.h> #include <linux/log2.h> #include <linux/hwspinlock.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #define CREATE_TRACE_POINTS #include "trace.h" @@ -89,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) @@ -112,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); @@ -129,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) @@ -598,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) { @@ -745,6 +756,7 @@ struct regmap *__regmap_init(struct device *dev, lock_key, lock_name); } map->lock_arg = map; + map->lock_key = lock_key; } /* @@ -757,14 +769,13 @@ struct regmap *__regmap_init(struct device *dev, map->alloc_flags = GFP_KERNEL; map->reg_base = config->reg_base; + map->reg_shift = config->pad_bits % 8; - map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.pad_bytes = config->pad_bits / 8; map->format.reg_shift = config->reg_shift; - 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_shift = config->pad_bits % 8; + 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 @@ -787,6 +798,7 @@ struct regmap *__regmap_init(struct device *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; @@ -1050,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; } @@ -1412,6 +1424,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) 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; @@ -1442,7 +1455,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) @@ -2345,7 +2360,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; @@ -2837,6 +2852,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 @@ -3062,8 +3114,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 @@ -3093,47 +3190,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 { - 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) { - 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) @@ -3383,7 +3468,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); diff --git a/drivers/base/regmap/trace.h b/drivers/base/regmap/trace.h index 704e106e5dbd..bcc5a8b226a6 100644 --- a/drivers/base/regmap/trace.h +++ b/drivers/base/regmap/trace.h @@ -27,7 +27,7 @@ DECLARE_EVENT_CLASS(regmap_reg, ), TP_fast_assign( - __assign_str(name, regmap_name(map)); + __assign_str(name); __entry->reg = reg; __entry->val = val; ), @@ -74,7 +74,7 @@ DECLARE_EVENT_CLASS(regmap_bulk, ), TP_fast_assign( - __assign_str(name, regmap_name(map)); + __assign_str(name); __entry->reg = reg; __entry->val_len = val_len; memcpy(__get_dynamic_array(buf), val, val_len); @@ -113,7 +113,7 @@ DECLARE_EVENT_CLASS(regmap_block, ), TP_fast_assign( - __assign_str(name, regmap_name(map)); + __assign_str(name); __entry->reg = reg; __entry->count = count; ), @@ -163,9 +163,9 @@ TRACE_EVENT(regcache_sync, ), TP_fast_assign( - __assign_str(name, regmap_name(map)); - __assign_str(status, status); - __assign_str(type, type); + __assign_str(name); + __assign_str(status); + __assign_str(type); ), TP_printk("%s type=%s status=%s", __get_str(name), @@ -184,7 +184,7 @@ DECLARE_EVENT_CLASS(regmap_bool, ), TP_fast_assign( - __assign_str(name, regmap_name(map)); + __assign_str(name); __entry->flag = flag; ), @@ -216,7 +216,7 @@ DECLARE_EVENT_CLASS(regmap_async, ), TP_fast_assign( - __assign_str(name, regmap_name(map)); + __assign_str(name); ), TP_printk("%s", __get_str(name)) @@ -264,7 +264,7 @@ TRACE_EVENT(regcache_drop_region, ), TP_fast_assign( - __assign_str(name, regmap_name(map)); + __assign_str(name); __entry->from = from; __entry->to = to; ), |