diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/component.c | 8 | ||||
-rw-r--r-- | drivers/base/dma-buf.c | 25 | ||||
-rw-r--r-- | drivers/base/firmware_class.c | 1 | ||||
-rw-r--r-- | drivers/base/regmap/internal.h | 2 | ||||
-rw-r--r-- | drivers/base/regmap/regcache.c | 13 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 2 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-irq.c | 6 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-mmio.c | 56 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 312 |
9 files changed, 347 insertions, 78 deletions
diff --git a/drivers/base/component.c b/drivers/base/component.c index c53efe6c6d8..c4778995cd7 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -133,9 +133,16 @@ static int try_to_bring_up_master(struct master *master, goto out; } + if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) { + ret = -ENOMEM; + goto out; + } + /* Found all components */ ret = master->ops->bind(master->dev); if (ret < 0) { + devres_release_group(master->dev, NULL); + dev_info(master->dev, "master bind failed: %d\n", ret); master_remove_components(master); goto out; } @@ -166,6 +173,7 @@ static void take_down_master(struct master *master) { if (master->bound) { master->ops->unbind(master->dev); + devres_release_group(master->dev, NULL); master->bound = false; } diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index 1e16cbd61da..61d6d62cc0d 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c @@ -616,36 +616,35 @@ static int dma_buf_describe(struct seq_file *s) if (ret) return ret; - seq_printf(s, "\nDma-buf Objects:\n"); - seq_printf(s, "\texp_name\tsize\tflags\tmode\tcount\n"); + seq_puts(s, "\nDma-buf Objects:\n"); + seq_puts(s, "size\tflags\tmode\tcount\texp_name\n"); list_for_each_entry(buf_obj, &db_list.head, list_node) { ret = mutex_lock_interruptible(&buf_obj->lock); if (ret) { - seq_printf(s, - "\tERROR locking buffer object: skipping\n"); + seq_puts(s, + "\tERROR locking buffer object: skipping\n"); continue; } - seq_printf(s, "\t"); - - seq_printf(s, "\t%s\t%08zu\t%08x\t%08x\t%08ld\n", - buf_obj->exp_name, buf_obj->size, + seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%s\n", + buf_obj->size, buf_obj->file->f_flags, buf_obj->file->f_mode, - (long)(buf_obj->file->f_count.counter)); + (long)(buf_obj->file->f_count.counter), + buf_obj->exp_name); - seq_printf(s, "\t\tAttached Devices:\n"); + seq_puts(s, "\tAttached Devices:\n"); attach_count = 0; list_for_each_entry(attach_obj, &buf_obj->attachments, node) { - seq_printf(s, "\t\t"); + seq_puts(s, "\t"); - seq_printf(s, "%s\n", attach_obj->dev->init_name); + seq_printf(s, "%s\n", dev_name(attach_obj->dev)); attach_count++; } - seq_printf(s, "\n\t\tTotal %d devices attached\n", + seq_printf(s, "Total %d devices attached\n\n", attach_count); count++; diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 8a97ddfa612..c30df50e444 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -1580,6 +1580,7 @@ static int fw_pm_notify(struct notifier_block *notify_block, switch (mode) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: + case PM_RESTORE_PREPARE: kill_requests_without_uevent(); device_cache_fw_images(); break; diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 33414b1de20..7d1326985be 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -134,6 +134,8 @@ struct regmap { /* if set, converts bulk rw to single rw */ bool use_single_rw; + /* if set, the device supports multi write mode */ + bool can_multi_write; struct rb_root range_tree; void *selector_work_buf; /* Scratch buffer used for selector */ diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index d4dd7713481..29b4128da0b 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -249,11 +249,12 @@ static int regcache_default_sync(struct regmap *map, unsigned int min, { unsigned int reg; - for (reg = min; reg <= max; reg++) { + for (reg = min; reg <= max; reg += map->reg_stride) { unsigned int val; int ret; - if (regmap_volatile(map, reg)) + if (regmap_volatile(map, reg) || + !regmap_writeable(map, reg)) continue; ret = regcache_read(map, reg, &val); @@ -312,10 +313,6 @@ int regcache_sync(struct regmap *map) /* Apply any patch first */ map->cache_bypass = 1; for (i = 0; i < map->patch_regs; i++) { - if (map->patch[i].reg % map->reg_stride) { - ret = -EINVAL; - goto out; - } ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def); if (ret != 0) { dev_err(map->dev, "Failed to write %x = %x: %d\n", @@ -636,10 +633,10 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, if (*data == NULL) return 0; - count = cur - base; + count = (cur - base) / map->reg_stride; dev_dbg(map->dev, "Writing %zu bytes for %d registers from 0x%x-0x%x\n", - count * val_bytes, count, base, cur - 1); + count * val_bytes, count, base, cur - map->reg_stride); map->cache_bypass = 1; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index c5471cd6ebb..45d812c0ea7 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -511,7 +511,7 @@ void regmap_debugfs_init(struct regmap *map, const char *name) debugfs_create_file("range", 0400, map->debugfs, map, ®map_reg_ranges_fops); - if (map->max_register) { + if (map->max_register || regmap_readable(map, 0)) { debugfs_create_file("registers", 0400, map->debugfs, map, ®map_map_fops); debugfs_create_file("access", 0400, map->debugfs, diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 82692068d3c..edf88f20cbc 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -368,8 +368,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!d) return -ENOMEM; - *data = d; - d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, GFP_KERNEL); if (!d->status_buf) @@ -506,6 +504,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, goto err_domain; } + *data = d; + return 0; err_domain: @@ -533,7 +533,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) return; free_irq(irq, d); - /* We should unmap the domain but... */ + irq_domain_remove(d->domain); kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 81f97751077..de45a1e1548 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -26,10 +26,47 @@ struct regmap_mmio_context { void __iomem *regs; + unsigned reg_bytes; unsigned val_bytes; + unsigned pad_bytes; struct clk *clk; }; +static inline void regmap_mmio_regsize_check(size_t reg_size) +{ + switch (reg_size) { + case 1: + case 2: + case 4: +#ifdef CONFIG_64BIT + case 8: +#endif + break; + default: + BUG(); + } +} + +static int regmap_mmio_regbits_check(size_t reg_bits) +{ + switch (reg_bits) { + case 8: + case 16: + case 32: +#ifdef CONFIG_64BIT + case 64: +#endif + return 0; + default: + return -EINVAL; + } +} + +static inline void regmap_mmio_count_check(size_t count) +{ + BUG_ON(count % 2 != 0); +} + static int regmap_mmio_gather_write(void *context, const void *reg, size_t reg_size, const void *val, size_t val_size) @@ -38,7 +75,7 @@ static int regmap_mmio_gather_write(void *context, u32 offset; int ret; - BUG_ON(reg_size != 4); + regmap_mmio_regsize_check(reg_size); if (!IS_ERR(ctx->clk)) { ret = clk_enable(ctx->clk); @@ -81,9 +118,13 @@ static int regmap_mmio_gather_write(void *context, static int regmap_mmio_write(void *context, const void *data, size_t count) { - BUG_ON(count < 4); + struct regmap_mmio_context *ctx = context; + u32 offset = ctx->reg_bytes + ctx->pad_bytes; + + regmap_mmio_count_check(count); - return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4); + return regmap_mmio_gather_write(context, data, ctx->reg_bytes, + data + offset, count - offset); } static int regmap_mmio_read(void *context, @@ -94,7 +135,7 @@ static int regmap_mmio_read(void *context, u32 offset; int ret; - BUG_ON(reg_size != 4); + regmap_mmio_regsize_check(reg_size); if (!IS_ERR(ctx->clk)) { ret = clk_enable(ctx->clk); @@ -165,8 +206,9 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, int min_stride; int ret; - if (config->reg_bits != 32) - return ERR_PTR(-EINVAL); + ret = regmap_mmio_regbits_check(config->reg_bits); + if (ret) + return ERR_PTR(ret); if (config->pad_bits) return ERR_PTR(-EINVAL); @@ -209,6 +251,8 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, ctx->regs = regs; ctx->val_bytes = config->val_bits / 8; + ctx->reg_bytes = config->reg_bits / 8; + ctx->pad_bytes = config->pad_bits / 8; ctx->clk = ERR_PTR(-ENODEV); if (clk_id == NULL) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 43065ceff90..d0a072463a0 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -461,6 +461,7 @@ struct regmap *regmap_init(struct device *dev, else map->reg_stride = 1; map->use_single_rw = config->use_single_rw; + map->can_multi_write = config->can_multi_write; map->dev = dev; map->bus = bus; map->bus_context = bus_context; @@ -740,7 +741,7 @@ skip_format_initialization: new->window_start = range_cfg->window_start; new->window_len = range_cfg->window_len; - if (_regmap_range_add(map, new) == false) { + if (!_regmap_range_add(map, new)) { dev_err(map->dev, "Failed to add range %d\n", i); kfree(new); goto err_range; @@ -1535,12 +1536,12 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (reg % map->reg_stride) return -EINVAL; - map->lock(map->lock_arg); /* * Some devices don't support bulk write, for * them we have a series of single write operations. */ if (!map->bus || map->use_single_rw) { + map->lock(map->lock_arg); for (i = 0; i < val_count; i++) { unsigned int ival; @@ -1569,31 +1570,239 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (ret != 0) goto out; } +out: + map->unlock(map->lock_arg); } else { void *wval; wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL); if (!wval) { - ret = -ENOMEM; dev_err(map->dev, "Error in memory allocation\n"); - goto out; + return -ENOMEM; } for (i = 0; i < val_count * val_bytes; i += val_bytes) map->format.parse_inplace(wval + i); + map->lock(map->lock_arg); ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); + map->unlock(map->lock_arg); kfree(wval); } -out: - map->unlock(map->lock_arg); return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_write); /* + * _regmap_raw_multi_reg_write() + * + * the (register,newvalue) pairs in regs have not been formatted, but + * they are all in the same page and have been changed to being page + * relative. The page register has been written if that was neccessary. + */ +static int _regmap_raw_multi_reg_write(struct regmap *map, + const struct reg_default *regs, + size_t num_regs) +{ + int ret; + void *buf; + int i; + u8 *u8; + size_t val_bytes = map->format.val_bytes; + size_t reg_bytes = map->format.reg_bytes; + size_t pad_bytes = map->format.pad_bytes; + size_t pair_size = reg_bytes + pad_bytes + val_bytes; + size_t len = pair_size * num_regs; + + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* We have to linearise by hand. */ + + u8 = buf; + + for (i = 0; i < num_regs; i++) { + int reg = regs[i].reg; + int val = regs[i].def; + trace_regmap_hw_write_start(map->dev, reg, 1); + map->format.format_reg(u8, reg, map->reg_shift); + u8 += reg_bytes + pad_bytes; + map->format.format_val(u8, val, 0); + u8 += val_bytes; + } + u8 = buf; + *u8 |= map->write_flag_mask; + + ret = map->bus->write(map->bus_context, buf, len); + + kfree(buf); + + for (i = 0; i < num_regs; i++) { + int reg = regs[i].reg; + trace_regmap_hw_write_done(map->dev, reg, 1); + } + return ret; +} + +static unsigned int _regmap_register_page(struct regmap *map, + unsigned int reg, + struct regmap_range_node *range) +{ + unsigned int win_page = (reg - range->range_min) / range->window_len; + + return win_page; +} + +static int _regmap_range_multi_paged_reg_write(struct regmap *map, + struct reg_default *regs, + size_t num_regs) +{ + int ret; + int i, n; + struct reg_default *base; + unsigned int this_page; + /* + * the set of registers are not neccessarily in order, but + * since the order of write must be preserved this algorithm + * chops the set each time the page changes + */ + base = regs; + for (i = 0, n = 0; i < num_regs; i++, n++) { + unsigned int reg = regs[i].reg; + struct regmap_range_node *range; + + range = _regmap_range_lookup(map, reg); + if (range) { + unsigned int win_page = _regmap_register_page(map, reg, + range); + + if (i == 0) + this_page = win_page; + if (win_page != this_page) { + this_page = win_page; + ret = _regmap_raw_multi_reg_write(map, base, n); + if (ret != 0) + return ret; + base += n; + n = 0; + } + ret = _regmap_select_page(map, &base[n].reg, range, 1); + if (ret != 0) + return ret; + } + } + if (n > 0) + return _regmap_raw_multi_reg_write(map, base, n); + return 0; +} + +static int _regmap_multi_reg_write(struct regmap *map, + const struct reg_default *regs, + size_t num_regs) +{ + int i; + int ret; + + if (!map->can_multi_write) { + for (i = 0; i < num_regs; i++) { + ret = _regmap_write(map, regs[i].reg, regs[i].def); + if (ret != 0) + return ret; + } + return 0; + } + + if (!map->format.parse_inplace) + return -EINVAL; + + if (map->writeable_reg) + for (i = 0; i < num_regs; i++) { + int reg = regs[i].reg; + if (!map->writeable_reg(map->dev, reg)) + return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; + } + + if (!map->cache_bypass) { + for (i = 0; i < num_regs; i++) { + unsigned int val = regs[i].def; + unsigned int reg = regs[i].reg; + ret = regcache_write(map, reg, val); + if (ret) { + dev_err(map->dev, + "Error in caching of register: %x ret: %d\n", + reg, ret); + return ret; + } + } + if (map->cache_only) { + map->cache_dirty = true; + return 0; + } + } + + WARN_ON(!map->bus); + + for (i = 0; i < num_regs; i++) { + unsigned int reg = regs[i].reg; + struct regmap_range_node *range; + range = _regmap_range_lookup(map, reg); + if (range) { + size_t len = sizeof(struct reg_default)*num_regs; + struct reg_default *base = kmemdup(regs, len, + GFP_KERNEL); + if (!base) + return -ENOMEM; + ret = _regmap_range_multi_paged_reg_write(map, base, + num_regs); + kfree(base); + + return ret; + } + } + return _regmap_raw_multi_reg_write(map, regs, num_regs); +} + +/* * regmap_multi_reg_write(): Write multiple registers to the device * + * where the set of register,value pairs are supplied in any order, + * possibly not all in a single range. + * + * @map: Register map to write to + * @regs: Array of structures containing register,value to be written + * @num_regs: Number of registers to write + * + * The 'normal' block write mode will send ultimately send data on the + * target bus as R,V1,V2,V3,..,Vn where successively higer registers are + * addressed. However, this alternative block multi write mode will send + * the data as R1,V1,R2,V2,..,Rn,Vn on the target bus. The target device + * must of course support the mode. + * + * A value of zero will be returned on success, a negative errno will be + * returned in error cases. + */ +int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, + int num_regs) +{ + int ret; + + map->lock(map->lock_arg); + + ret = _regmap_multi_reg_write(map, regs, num_regs); + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_multi_reg_write); + +/* + * regmap_multi_reg_write_bypassed(): Write multiple registers to the + * device but not the cache + * * where the set of register are supplied in any order * * @map: Register map to write to @@ -1607,30 +1816,27 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write); * A value of zero will be returned on success, a negative errno will * be returned in error cases. */ -int regmap_multi_reg_write(struct regmap *map, struct reg_default *regs, - int num_regs) +int regmap_multi_reg_write_bypassed(struct regmap *map, + const struct reg_default *regs, + int num_regs) { - int ret = 0, i; - - for (i = 0; i < num_regs; i++) { - int reg = regs[i].reg; - if (reg % map->reg_stride) - return -EINVAL; - } + int ret; + bool bypass; map->lock(map->lock_arg); - for (i = 0; i < num_regs; i++) { - ret = _regmap_write(map, regs[i].reg, regs[i].def); - if (ret != 0) - goto out; - } -out: + bypass = map->cache_bypass; + map->cache_bypass = true; + + ret = _regmap_multi_reg_write(map, regs, num_regs); + + map->cache_bypass = bypass; + map->unlock(map->lock_arg); return ret; } -EXPORT_SYMBOL_GPL(regmap_multi_reg_write); +EXPORT_SYMBOL_GPL(regmap_multi_reg_write_bypassed); /** * regmap_raw_write_async(): Write raw values to one or more registers @@ -1751,6 +1957,9 @@ static int _regmap_read(struct regmap *map, unsigned int reg, if (map->cache_only) return -EBUSY; + if (!regmap_readable(map, reg)) + return -EIO; + ret = map->reg_read(context, reg, val); if (ret == 0) { #ifdef LOG_DEVICE @@ -1981,9 +2190,11 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, if (tmp != orig) { ret = _regmap_write(map, reg, tmp); - *change = true; + if (change) + *change = true; } else { - *change = false; + if (change) + *change = false; } return ret; @@ -2002,11 +2213,10 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, int regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val) { - bool change; int ret; map->lock(map->lock_arg); - ret = _regmap_update_bits(map, reg, mask, val, &change); + ret = _regmap_update_bits(map, reg, mask, val, NULL); map->unlock(map->lock_arg); return ret; @@ -2031,14 +2241,13 @@ EXPORT_SYMBOL_GPL(regmap_update_bits); int regmap_update_bits_async(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val) { - bool change; int ret; map->lock(map->lock_arg); map->async = true; - ret = _regmap_update_bits(map, reg, mask, val, &change); + ret = _regmap_update_bits(map, reg, mask, val, NULL); map->async = false; @@ -2188,35 +2397,21 @@ EXPORT_SYMBOL_GPL(regmap_async_complete); * apply them immediately. Typically this is used to apply * corrections to be applied to the device defaults on startup, such * as the updates some vendors provide to undocumented registers. + * + * The caller must ensure that this function cannot be called + * concurrently with either itself or regcache_sync(). */ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs) { struct reg_default *p; - int i, ret; + int ret; bool bypass; if (WARN_ONCE(num_regs <= 0, "invalid registers number (%d)\n", num_regs)) return 0; - map->lock(map->lock_arg); - - bypass = map->cache_bypass; - - map->cache_bypass = true; - map->async = true; - - /* Write out first; it's useful to apply even if we fail later. */ - for (i = 0; i < num_regs; i++) { - ret = _regmap_write(map, regs[i].reg, regs[i].def); - if (ret != 0) { - dev_err(map->dev, "Failed to write %x = %x: %d\n", - regs[i].reg, regs[i].def, ret); - goto out; - } - } - p = krealloc(map->patch, sizeof(struct reg_default) * (map->patch_regs + num_regs), GFP_KERNEL); @@ -2225,9 +2420,20 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, map->patch = p; map->patch_regs += num_regs; } else { - ret = -ENOMEM; + return -ENOMEM; } + map->lock(map->lock_arg); + + bypass = map->cache_bypass; + + map->cache_bypass = true; + map->async = true; + + ret = _regmap_multi_reg_write(map, regs, num_regs); + if (ret != 0) + goto out; + out: map->async = false; map->cache_bypass = bypass; @@ -2255,6 +2461,18 @@ int regmap_get_val_bytes(struct regmap *map) } EXPORT_SYMBOL_GPL(regmap_get_val_bytes); +int regmap_parse_val(struct regmap *map, const void *buf, + unsigned int *val) +{ + if (!map->format.parse_val) + return -EINVAL; + + *val = map->format.parse_val(buf); + + return 0; +} +EXPORT_SYMBOL_GPL(regmap_parse_val); + static int __init regmap_initcall(void) { regmap_debugfs_initcall(); |