Loading drivers/base/regmap/internal.h +2 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading drivers/base/regmap/regcache.c +3 −6 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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", Loading drivers/base/regmap/regmap-irq.c +3 −3 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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: Loading Loading @@ -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); Loading drivers/base/regmap/regmap-mmio.c +50 −6 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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); Loading Loading @@ -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, Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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) Loading drivers/base/regmap/regmap.c +283 −53 Original line number Diff line number Diff line Loading @@ -380,6 +380,28 @@ static void regmap_range_exit(struct regmap *map) kfree(map->selector_work_buf); } int regmap_attach_dev(struct device *dev, struct regmap *map, const struct regmap_config *config) { struct regmap **m; map->dev = dev; regmap_debugfs_init(map, config->name); /* Add a devres resource for dev_get_regmap() */ m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); if (!m) { regmap_debugfs_exit(map); return -ENOMEM; } *m = map; devres_add(dev, m); return 0; } EXPORT_SYMBOL_GPL(regmap_attach_dev); /** * regmap_init(): Initialise register map * Loading @@ -397,7 +419,7 @@ struct regmap *regmap_init(struct device *dev, void *bus_context, const struct regmap_config *config) { struct regmap *map, **m; struct regmap *map; int ret = -EINVAL; enum regmap_endian reg_endian, val_endian; int i, j; Loading Loading @@ -439,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; Loading Loading @@ -734,25 +757,18 @@ struct regmap *regmap_init(struct device *dev, } } regmap_debugfs_init(map, config->name); ret = regcache_init(map, config); if (ret != 0) goto err_range; /* Add a devres resource for dev_get_regmap() */ m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); if (!m) { ret = -ENOMEM; goto err_debugfs; } *m = map; devres_add(dev, m); if (dev) ret = regmap_attach_dev(dev, map, config); if (ret != 0) goto err_regcache; return map; err_debugfs: regmap_debugfs_exit(map); err_regcache: regcache_exit(map); err_range: regmap_range_exit(map); Loading Loading @@ -1520,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; Loading Loading @@ -1554,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 Loading @@ -1592,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 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 Loading Loading @@ -2176,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); Loading @@ -2213,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; Loading Loading @@ -2243,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(); Loading Loading
drivers/base/regmap/internal.h +2 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading
drivers/base/regmap/regcache.c +3 −6 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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", Loading
drivers/base/regmap/regmap-irq.c +3 −3 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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: Loading Loading @@ -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); Loading
drivers/base/regmap/regmap-mmio.c +50 −6 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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); Loading Loading @@ -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, Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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) Loading
drivers/base/regmap/regmap.c +283 −53 Original line number Diff line number Diff line Loading @@ -380,6 +380,28 @@ static void regmap_range_exit(struct regmap *map) kfree(map->selector_work_buf); } int regmap_attach_dev(struct device *dev, struct regmap *map, const struct regmap_config *config) { struct regmap **m; map->dev = dev; regmap_debugfs_init(map, config->name); /* Add a devres resource for dev_get_regmap() */ m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); if (!m) { regmap_debugfs_exit(map); return -ENOMEM; } *m = map; devres_add(dev, m); return 0; } EXPORT_SYMBOL_GPL(regmap_attach_dev); /** * regmap_init(): Initialise register map * Loading @@ -397,7 +419,7 @@ struct regmap *regmap_init(struct device *dev, void *bus_context, const struct regmap_config *config) { struct regmap *map, **m; struct regmap *map; int ret = -EINVAL; enum regmap_endian reg_endian, val_endian; int i, j; Loading Loading @@ -439,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; Loading Loading @@ -734,25 +757,18 @@ struct regmap *regmap_init(struct device *dev, } } regmap_debugfs_init(map, config->name); ret = regcache_init(map, config); if (ret != 0) goto err_range; /* Add a devres resource for dev_get_regmap() */ m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); if (!m) { ret = -ENOMEM; goto err_debugfs; } *m = map; devres_add(dev, m); if (dev) ret = regmap_attach_dev(dev, map, config); if (ret != 0) goto err_regcache; return map; err_debugfs: regmap_debugfs_exit(map); err_regcache: regcache_exit(map); err_range: regmap_range_exit(map); Loading Loading @@ -1520,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; Loading Loading @@ -1554,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 Loading @@ -1592,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 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 Loading Loading @@ -2176,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); Loading @@ -2213,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; Loading Loading @@ -2243,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(); Loading