Loading drivers/base/regmap/internal.h +18 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/regmap.h> #include <linux/fs.h> #include <linux/list.h> #include <linux/wait.h> struct regmap; struct regcache_ops; Loading @@ -39,6 +40,13 @@ struct regmap_format { unsigned int (*parse_val)(void *buf); }; struct regmap_async { struct list_head list; struct work_struct cleanup; struct regmap *map; void *work_buf; }; struct regmap { struct mutex mutex; spinlock_t spinlock; Loading @@ -53,6 +61,11 @@ struct regmap { void *bus_context; const char *name; spinlock_t async_lock; wait_queue_head_t async_waitq; struct list_head async_list; int async_ret; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; const char *debugfs_name; Loading @@ -74,6 +87,9 @@ struct regmap { const struct regmap_access_table *volatile_table; const struct regmap_access_table *precious_table; int (*reg_read)(void *context, unsigned int reg, unsigned int *val); int (*reg_write)(void *context, unsigned int reg, unsigned int val); u8 read_flag_mask; u8 write_flag_mask; Loading Loading @@ -175,6 +191,8 @@ bool regcache_set_val(void *base, unsigned int idx, unsigned int val, unsigned int word_size); int regcache_lookup_reg(struct regmap *map, unsigned int reg); void regmap_async_complete_cb(struct regmap_async *async, int ret); extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; Loading drivers/base/regmap/regmap-spi.c +54 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,21 @@ #include <linux/init.h> #include <linux/module.h> #include "internal.h" struct regmap_async_spi { struct regmap_async core; struct spi_message m; struct spi_transfer t[2]; }; static void regmap_spi_complete(void *data) { struct regmap_async_spi *async = data; regmap_async_complete_cb(&async->core, async->m.status); } static int regmap_spi_write(void *context, const void *data, size_t count) { struct device *dev = context; Loading @@ -40,6 +55,43 @@ static int regmap_spi_gather_write(void *context, return spi_sync(spi, &m); } static int regmap_spi_async_write(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len, struct regmap_async *a) { struct regmap_async_spi *async = container_of(a, struct regmap_async_spi, core); struct device *dev = context; struct spi_device *spi = to_spi_device(dev); async->t[0].tx_buf = reg; async->t[0].len = reg_len; async->t[1].tx_buf = val; async->t[1].len = val_len; spi_message_init(&async->m); spi_message_add_tail(&async->t[0], &async->m); spi_message_add_tail(&async->t[1], &async->m); async->m.complete = regmap_spi_complete; async->m.context = async; return spi_async(spi, &async->m); } static struct regmap_async *regmap_spi_async_alloc(void) { struct regmap_async_spi *async_spi; async_spi = kzalloc(sizeof(*async_spi), GFP_KERNEL); if (!async_spi) return NULL; return &async_spi->core; } static int regmap_spi_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) Loading @@ -53,6 +105,8 @@ static int regmap_spi_read(void *context, static struct regmap_bus regmap_spi = { .write = regmap_spi_write, .gather_write = regmap_spi_gather_write, .async_write = regmap_spi_async_write, .async_alloc = regmap_spi_async_alloc, .read = regmap_spi_read, .read_flag_mask = 0x80, }; Loading drivers/base/regmap/regmap.c +254 −47 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/mutex.h> #include <linux/err.h> #include <linux/rbtree.h> #include <linux/sched.h> #define CREATE_TRACE_POINTS #include <trace/events/regmap.h> Loading @@ -34,6 +35,22 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); static int _regmap_bus_read(void *context, unsigned int reg, unsigned int *val); static int _regmap_bus_formatted_write(void *context, unsigned int reg, unsigned int val); static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val); static void async_cleanup(struct work_struct *work) { struct regmap_async *async = container_of(work, struct regmap_async, cleanup); kfree(async->work_buf); kfree(async); } bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges) Loading Loading @@ -423,6 +440,10 @@ struct regmap *regmap_init(struct device *dev, map->cache_type = config->cache_type; map->name = config->name; spin_lock_init(&map->async_lock); INIT_LIST_HEAD(&map->async_list); init_waitqueue_head(&map->async_waitq); if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; map->write_flag_mask = config->write_flag_mask; Loading @@ -430,6 +451,8 @@ struct regmap *regmap_init(struct device *dev, map->read_flag_mask = bus->read_flag_mask; } map->reg_read = _regmap_bus_read; reg_endian = config->reg_format_endian; if (reg_endian == REGMAP_ENDIAN_DEFAULT) reg_endian = bus->reg_format_endian_default; Loading Loading @@ -581,6 +604,11 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } if (map->format.format_write) map->reg_write = _regmap_bus_formatted_write; else if (map->format.format_val) map->reg_write = _regmap_bus_raw_write; map->range_tree = RB_ROOT; for (i = 0; i < config->num_ranges; i++) { const struct regmap_range_cfg *range_cfg = &config->ranges[i]; Loading Loading @@ -876,10 +904,13 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, } static int _regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) const void *val, size_t val_len, bool async) { struct regmap_range_node *range; unsigned long flags; u8 *u8 = map->work_buf; void *work_val = map->work_buf + map->format.reg_bytes + map->format.pad_bytes; void *buf; int ret = -ENOTSUPP; size_t len; Loading Loading @@ -924,7 +955,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, dev_dbg(map->dev, "Writing window %d/%zu\n", win_residue, val_len / map->format.val_bytes); ret = _regmap_raw_write(map, reg, val, win_residue * map->format.val_bytes); map->format.val_bytes, async); if (ret != 0) return ret; Loading @@ -947,6 +978,50 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, u8[0] |= map->write_flag_mask; if (async && map->bus->async_write) { struct regmap_async *async = map->bus->async_alloc(); if (!async) return -ENOMEM; async->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL | GFP_DMA); if (!async->work_buf) { kfree(async); return -ENOMEM; } INIT_WORK(&async->cleanup, async_cleanup); async->map = map; /* If the caller supplied the value we can use it safely. */ memcpy(async->work_buf, map->work_buf, map->format.pad_bytes + map->format.reg_bytes + map->format.val_bytes); if (val == work_val) val = async->work_buf + map->format.pad_bytes + map->format.reg_bytes; spin_lock_irqsave(&map->async_lock, flags); list_add_tail(&async->list, &map->async_list); spin_unlock_irqrestore(&map->async_lock, flags); ret = map->bus->async_write(map->bus_context, async->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len, async); if (ret != 0) { dev_err(map->dev, "Failed to schedule write: %d\n", ret); spin_lock_irqsave(&map->async_lock, flags); list_del(&async->list); spin_unlock_irqrestore(&map->async_lock, flags); kfree(async->work_buf); kfree(async); } } trace_regmap_hw_write_start(map->dev, reg, val_len / map->format.val_bytes); Loading @@ -954,8 +1029,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, * send the work_buf directly, otherwise try to do a gather * write. */ if (val == (map->work_buf + map->format.pad_bytes + map->format.reg_bytes)) if (val == work_val) ret = map->bus->write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes + Loading Loading @@ -987,31 +1061,15 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, return ret; } int _regmap_write(struct regmap *map, unsigned int reg, static int _regmap_bus_formatted_write(void *context, unsigned int reg, unsigned int val) { struct regmap_range_node *range; int ret; BUG_ON(!map->format.format_write && !map->format.format_val); if (!map->cache_bypass && map->format.format_write) { ret = regcache_write(map, reg, val); if (ret != 0) return ret; if (map->cache_only) { map->cache_dirty = true; return 0; } } #ifdef LOG_DEVICE if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) dev_info(map->dev, "%x <= %x\n", reg, val); #endif struct regmap_range_node *range; struct regmap *map = context; trace_regmap_reg_write(map->dev, reg, val); BUG_ON(!map->format.format_write); if (map->format.format_write) { range = _regmap_range_lookup(map, reg); if (range) { ret = _regmap_select_page(map, ®, range, 1); Loading @@ -1029,15 +1087,47 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_hw_write_done(map->dev, reg, 1); return ret; } else { } static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val) { struct regmap *map = context; BUG_ON(!map->format.format_val); map->format.format_val(map->work_buf + map->format.reg_bytes + map->format.pad_bytes, val, 0); return _regmap_raw_write(map, reg, map->work_buf + map->format.reg_bytes + map->format.pad_bytes, map->format.val_bytes); map->format.val_bytes, false); } int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; if (!map->cache_bypass && map->format.format_write) { ret = regcache_write(map, reg, val); if (ret != 0) return ret; if (map->cache_only) { map->cache_dirty = true; return 0; } } #ifdef LOG_DEVICE if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) dev_info(map->dev, "%x <= %x\n", reg, val); #endif trace_regmap_reg_write(map->dev, reg, val); return map->reg_write(map, reg, val); } /** Loading Loading @@ -1095,7 +1185,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, map->lock(map->lock_arg); ret = _regmap_raw_write(map, reg, val, val_len); ret = _regmap_raw_write(map, reg, val, val_len, false); map->unlock(map->lock_arg); Loading Loading @@ -1158,7 +1248,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, return ret; } } else { ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count, false); } if (val_bytes != 1) Loading @@ -1170,6 +1261,48 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, } EXPORT_SYMBOL_GPL(regmap_bulk_write); /** * regmap_raw_write_async(): Write raw values to one or more registers * asynchronously * * @map: Register map to write to * @reg: Initial register to write to * @val: Block of data to be written, laid out for direct transmission to the * device. Must be valid until regmap_async_complete() is called. * @val_len: Length of data pointed to by val. * * This function is intended to be used for things like firmware * download where a large block of data needs to be transferred to the * device. No formatting will be done on the data provided. * * If supported by the underlying bus the write will be scheduled * asynchronously, helping maximise I/O speed on higher speed buses * like SPI. regmap_async_complete() can be called to ensure that all * asynchrnous writes have been completed. * * A value of zero will be returned on success, a negative errno will * be returned in error cases. */ int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { int ret; if (val_len % map->format.val_bytes) return -EINVAL; if (reg % map->reg_stride) return -EINVAL; map->lock(map->lock_arg); ret = _regmap_raw_write(map, reg, val, val_len, true); map->unlock(map->lock_arg); return ret; } EXPORT_SYMBOL_GPL(regmap_raw_write_async); static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int val_len) { Loading Loading @@ -1208,10 +1341,27 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, return ret; } static int _regmap_bus_read(void *context, unsigned int reg, unsigned int *val) { int ret; struct regmap *map = context; if (!map->format.parse_val) return -EINVAL; ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); if (ret == 0) *val = map->format.parse_val(map->work_buf); return ret; } static int _regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; BUG_ON(!map->reg_read); if (!map->cache_bypass) { ret = regcache_read(map, reg, val); Loading @@ -1219,26 +1369,21 @@ static int _regmap_read(struct regmap *map, unsigned int reg, return 0; } if (!map->format.parse_val) return -EINVAL; if (map->cache_only) return -EBUSY; ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); ret = map->reg_read(map, reg, val); if (ret == 0) { *val = map->format.parse_val(map->work_buf); #ifdef LOG_DEVICE if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) dev_info(map->dev, "%x => %x\n", reg, *val); #endif trace_regmap_reg_read(map->dev, reg, *val); } if (ret == 0 && !map->cache_bypass) if (!map->cache_bypass) regcache_write(map, reg, *val); } return ret; } Loading Loading @@ -1456,6 +1601,68 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_update_bits_check); void regmap_async_complete_cb(struct regmap_async *async, int ret) { struct regmap *map = async->map; bool wake; spin_lock(&map->async_lock); list_del(&async->list); wake = list_empty(&map->async_list); if (ret != 0) map->async_ret = ret; spin_unlock(&map->async_lock); schedule_work(&async->cleanup); if (wake) wake_up(&map->async_waitq); } EXPORT_SYMBOL_GPL(regmap_async_complete_cb); static int regmap_async_is_done(struct regmap *map) { unsigned long flags; int ret; spin_lock_irqsave(&map->async_lock, flags); ret = list_empty(&map->async_list); spin_unlock_irqrestore(&map->async_lock, flags); return ret; } /** * regmap_async_complete: Ensure all asynchronous I/O has completed. * * @map: Map to operate on. * * Blocks until any pending asynchronous I/O has completed. Returns * an error code for any failed I/O operations. */ int regmap_async_complete(struct regmap *map) { unsigned long flags; int ret; /* Nothing to do with no async support */ if (!map->bus->async_write) return 0; wait_event(map->async_waitq, regmap_async_is_done(map)); spin_lock_irqsave(&map->async_lock, flags); ret = map->async_ret; map->async_ret = 0; spin_unlock_irqrestore(&map->async_lock, flags); return ret; } EXPORT_SYMBOL_GPL(regmap_async_complete); /** * regmap_register_patch: Register and apply register updates to be applied * on device initialistion Loading include/linux/regmap.h +28 −0 Original line number Diff line number Diff line Loading @@ -235,14 +235,21 @@ struct regmap_range_cfg { unsigned int window_len; }; struct regmap_async; typedef int (*regmap_hw_write)(void *context, const void *data, size_t count); typedef int (*regmap_hw_gather_write)(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len); typedef int (*regmap_hw_async_write)(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len, struct regmap_async *async); typedef int (*regmap_hw_read)(void *context, const void *reg_buf, size_t reg_size, void *val_buf, size_t val_size); typedef struct regmap_async *(*regmap_hw_async_alloc)(void); typedef void (*regmap_hw_free_context)(void *context); /** Loading @@ -255,8 +262,11 @@ typedef void (*regmap_hw_free_context)(void *context); * @write: Write operation. * @gather_write: Write operation with split register/value, return -ENOTSUPP * if not implemented on a given device. * @async_write: Write operation which completes asynchronously, optional and * must serialise with respect to non-async I/O. * @read: Read operation. Data is returned in the buffer used to transmit * data. * @async_alloc: Allocate a regmap_async() structure. * @read_flag_mask: Mask to be set in the top byte of the register when doing * a read. * @reg_format_endian_default: Default endianness for formatted register Loading @@ -265,13 +275,16 @@ typedef void (*regmap_hw_free_context)(void *context); * @val_format_endian_default: Default endianness for formatted register * values. Used when the regmap_config specifies DEFAULT. If this is * DEFAULT, BIG is assumed. * @async_size: Size of struct used for async work. */ struct regmap_bus { bool fast_io; regmap_hw_write write; regmap_hw_gather_write gather_write; regmap_hw_async_write async_write; regmap_hw_read read; regmap_hw_free_context free_context; regmap_hw_async_alloc async_alloc; u8 read_flag_mask; enum regmap_endian reg_format_endian_default; enum regmap_endian val_format_endian_default; Loading Loading @@ -310,6 +323,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count); int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val); int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len); Loading @@ -321,6 +336,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); int regmap_get_val_bytes(struct regmap *map); int regmap_async_complete(struct regmap *map); int regcache_sync(struct regmap *map); int regcache_sync_region(struct regmap *map, unsigned int min, Loading Loading @@ -422,6 +438,13 @@ static inline int regmap_raw_write(struct regmap *map, unsigned int reg, return -EINVAL; } static inline int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { WARN_ONCE(1, "regmap API is disabled"); return -EINVAL; } static inline int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count) { Loading Loading @@ -500,6 +523,11 @@ static inline void regcache_mark_dirty(struct regmap *map) WARN_ONCE(1, "regmap API is disabled"); } static inline void regmap_async_complete(struct regmap *map) { WARN_ONCE(1, "regmap API is disabled"); } static inline int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs) Loading Loading
drivers/base/regmap/internal.h +18 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/regmap.h> #include <linux/fs.h> #include <linux/list.h> #include <linux/wait.h> struct regmap; struct regcache_ops; Loading @@ -39,6 +40,13 @@ struct regmap_format { unsigned int (*parse_val)(void *buf); }; struct regmap_async { struct list_head list; struct work_struct cleanup; struct regmap *map; void *work_buf; }; struct regmap { struct mutex mutex; spinlock_t spinlock; Loading @@ -53,6 +61,11 @@ struct regmap { void *bus_context; const char *name; spinlock_t async_lock; wait_queue_head_t async_waitq; struct list_head async_list; int async_ret; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; const char *debugfs_name; Loading @@ -74,6 +87,9 @@ struct regmap { const struct regmap_access_table *volatile_table; const struct regmap_access_table *precious_table; int (*reg_read)(void *context, unsigned int reg, unsigned int *val); int (*reg_write)(void *context, unsigned int reg, unsigned int val); u8 read_flag_mask; u8 write_flag_mask; Loading Loading @@ -175,6 +191,8 @@ bool regcache_set_val(void *base, unsigned int idx, unsigned int val, unsigned int word_size); int regcache_lookup_reg(struct regmap *map, unsigned int reg); void regmap_async_complete_cb(struct regmap_async *async, int ret); extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; Loading
drivers/base/regmap/regmap-spi.c +54 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,21 @@ #include <linux/init.h> #include <linux/module.h> #include "internal.h" struct regmap_async_spi { struct regmap_async core; struct spi_message m; struct spi_transfer t[2]; }; static void regmap_spi_complete(void *data) { struct regmap_async_spi *async = data; regmap_async_complete_cb(&async->core, async->m.status); } static int regmap_spi_write(void *context, const void *data, size_t count) { struct device *dev = context; Loading @@ -40,6 +55,43 @@ static int regmap_spi_gather_write(void *context, return spi_sync(spi, &m); } static int regmap_spi_async_write(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len, struct regmap_async *a) { struct regmap_async_spi *async = container_of(a, struct regmap_async_spi, core); struct device *dev = context; struct spi_device *spi = to_spi_device(dev); async->t[0].tx_buf = reg; async->t[0].len = reg_len; async->t[1].tx_buf = val; async->t[1].len = val_len; spi_message_init(&async->m); spi_message_add_tail(&async->t[0], &async->m); spi_message_add_tail(&async->t[1], &async->m); async->m.complete = regmap_spi_complete; async->m.context = async; return spi_async(spi, &async->m); } static struct regmap_async *regmap_spi_async_alloc(void) { struct regmap_async_spi *async_spi; async_spi = kzalloc(sizeof(*async_spi), GFP_KERNEL); if (!async_spi) return NULL; return &async_spi->core; } static int regmap_spi_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) Loading @@ -53,6 +105,8 @@ static int regmap_spi_read(void *context, static struct regmap_bus regmap_spi = { .write = regmap_spi_write, .gather_write = regmap_spi_gather_write, .async_write = regmap_spi_async_write, .async_alloc = regmap_spi_async_alloc, .read = regmap_spi_read, .read_flag_mask = 0x80, }; Loading
drivers/base/regmap/regmap.c +254 −47 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/mutex.h> #include <linux/err.h> #include <linux/rbtree.h> #include <linux/sched.h> #define CREATE_TRACE_POINTS #include <trace/events/regmap.h> Loading @@ -34,6 +35,22 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); static int _regmap_bus_read(void *context, unsigned int reg, unsigned int *val); static int _regmap_bus_formatted_write(void *context, unsigned int reg, unsigned int val); static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val); static void async_cleanup(struct work_struct *work) { struct regmap_async *async = container_of(work, struct regmap_async, cleanup); kfree(async->work_buf); kfree(async); } bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges) Loading Loading @@ -423,6 +440,10 @@ struct regmap *regmap_init(struct device *dev, map->cache_type = config->cache_type; map->name = config->name; spin_lock_init(&map->async_lock); INIT_LIST_HEAD(&map->async_list); init_waitqueue_head(&map->async_waitq); if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; map->write_flag_mask = config->write_flag_mask; Loading @@ -430,6 +451,8 @@ struct regmap *regmap_init(struct device *dev, map->read_flag_mask = bus->read_flag_mask; } map->reg_read = _regmap_bus_read; reg_endian = config->reg_format_endian; if (reg_endian == REGMAP_ENDIAN_DEFAULT) reg_endian = bus->reg_format_endian_default; Loading Loading @@ -581,6 +604,11 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } if (map->format.format_write) map->reg_write = _regmap_bus_formatted_write; else if (map->format.format_val) map->reg_write = _regmap_bus_raw_write; map->range_tree = RB_ROOT; for (i = 0; i < config->num_ranges; i++) { const struct regmap_range_cfg *range_cfg = &config->ranges[i]; Loading Loading @@ -876,10 +904,13 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, } static int _regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) const void *val, size_t val_len, bool async) { struct regmap_range_node *range; unsigned long flags; u8 *u8 = map->work_buf; void *work_val = map->work_buf + map->format.reg_bytes + map->format.pad_bytes; void *buf; int ret = -ENOTSUPP; size_t len; Loading Loading @@ -924,7 +955,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, dev_dbg(map->dev, "Writing window %d/%zu\n", win_residue, val_len / map->format.val_bytes); ret = _regmap_raw_write(map, reg, val, win_residue * map->format.val_bytes); map->format.val_bytes, async); if (ret != 0) return ret; Loading @@ -947,6 +978,50 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, u8[0] |= map->write_flag_mask; if (async && map->bus->async_write) { struct regmap_async *async = map->bus->async_alloc(); if (!async) return -ENOMEM; async->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL | GFP_DMA); if (!async->work_buf) { kfree(async); return -ENOMEM; } INIT_WORK(&async->cleanup, async_cleanup); async->map = map; /* If the caller supplied the value we can use it safely. */ memcpy(async->work_buf, map->work_buf, map->format.pad_bytes + map->format.reg_bytes + map->format.val_bytes); if (val == work_val) val = async->work_buf + map->format.pad_bytes + map->format.reg_bytes; spin_lock_irqsave(&map->async_lock, flags); list_add_tail(&async->list, &map->async_list); spin_unlock_irqrestore(&map->async_lock, flags); ret = map->bus->async_write(map->bus_context, async->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len, async); if (ret != 0) { dev_err(map->dev, "Failed to schedule write: %d\n", ret); spin_lock_irqsave(&map->async_lock, flags); list_del(&async->list); spin_unlock_irqrestore(&map->async_lock, flags); kfree(async->work_buf); kfree(async); } } trace_regmap_hw_write_start(map->dev, reg, val_len / map->format.val_bytes); Loading @@ -954,8 +1029,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, * send the work_buf directly, otherwise try to do a gather * write. */ if (val == (map->work_buf + map->format.pad_bytes + map->format.reg_bytes)) if (val == work_val) ret = map->bus->write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes + Loading Loading @@ -987,31 +1061,15 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, return ret; } int _regmap_write(struct regmap *map, unsigned int reg, static int _regmap_bus_formatted_write(void *context, unsigned int reg, unsigned int val) { struct regmap_range_node *range; int ret; BUG_ON(!map->format.format_write && !map->format.format_val); if (!map->cache_bypass && map->format.format_write) { ret = regcache_write(map, reg, val); if (ret != 0) return ret; if (map->cache_only) { map->cache_dirty = true; return 0; } } #ifdef LOG_DEVICE if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) dev_info(map->dev, "%x <= %x\n", reg, val); #endif struct regmap_range_node *range; struct regmap *map = context; trace_regmap_reg_write(map->dev, reg, val); BUG_ON(!map->format.format_write); if (map->format.format_write) { range = _regmap_range_lookup(map, reg); if (range) { ret = _regmap_select_page(map, ®, range, 1); Loading @@ -1029,15 +1087,47 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_hw_write_done(map->dev, reg, 1); return ret; } else { } static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val) { struct regmap *map = context; BUG_ON(!map->format.format_val); map->format.format_val(map->work_buf + map->format.reg_bytes + map->format.pad_bytes, val, 0); return _regmap_raw_write(map, reg, map->work_buf + map->format.reg_bytes + map->format.pad_bytes, map->format.val_bytes); map->format.val_bytes, false); } int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; if (!map->cache_bypass && map->format.format_write) { ret = regcache_write(map, reg, val); if (ret != 0) return ret; if (map->cache_only) { map->cache_dirty = true; return 0; } } #ifdef LOG_DEVICE if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) dev_info(map->dev, "%x <= %x\n", reg, val); #endif trace_regmap_reg_write(map->dev, reg, val); return map->reg_write(map, reg, val); } /** Loading Loading @@ -1095,7 +1185,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, map->lock(map->lock_arg); ret = _regmap_raw_write(map, reg, val, val_len); ret = _regmap_raw_write(map, reg, val, val_len, false); map->unlock(map->lock_arg); Loading Loading @@ -1158,7 +1248,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, return ret; } } else { ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count, false); } if (val_bytes != 1) Loading @@ -1170,6 +1261,48 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, } EXPORT_SYMBOL_GPL(regmap_bulk_write); /** * regmap_raw_write_async(): Write raw values to one or more registers * asynchronously * * @map: Register map to write to * @reg: Initial register to write to * @val: Block of data to be written, laid out for direct transmission to the * device. Must be valid until regmap_async_complete() is called. * @val_len: Length of data pointed to by val. * * This function is intended to be used for things like firmware * download where a large block of data needs to be transferred to the * device. No formatting will be done on the data provided. * * If supported by the underlying bus the write will be scheduled * asynchronously, helping maximise I/O speed on higher speed buses * like SPI. regmap_async_complete() can be called to ensure that all * asynchrnous writes have been completed. * * A value of zero will be returned on success, a negative errno will * be returned in error cases. */ int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { int ret; if (val_len % map->format.val_bytes) return -EINVAL; if (reg % map->reg_stride) return -EINVAL; map->lock(map->lock_arg); ret = _regmap_raw_write(map, reg, val, val_len, true); map->unlock(map->lock_arg); return ret; } EXPORT_SYMBOL_GPL(regmap_raw_write_async); static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int val_len) { Loading Loading @@ -1208,10 +1341,27 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, return ret; } static int _regmap_bus_read(void *context, unsigned int reg, unsigned int *val) { int ret; struct regmap *map = context; if (!map->format.parse_val) return -EINVAL; ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); if (ret == 0) *val = map->format.parse_val(map->work_buf); return ret; } static int _regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; BUG_ON(!map->reg_read); if (!map->cache_bypass) { ret = regcache_read(map, reg, val); Loading @@ -1219,26 +1369,21 @@ static int _regmap_read(struct regmap *map, unsigned int reg, return 0; } if (!map->format.parse_val) return -EINVAL; if (map->cache_only) return -EBUSY; ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); ret = map->reg_read(map, reg, val); if (ret == 0) { *val = map->format.parse_val(map->work_buf); #ifdef LOG_DEVICE if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) dev_info(map->dev, "%x => %x\n", reg, *val); #endif trace_regmap_reg_read(map->dev, reg, *val); } if (ret == 0 && !map->cache_bypass) if (!map->cache_bypass) regcache_write(map, reg, *val); } return ret; } Loading Loading @@ -1456,6 +1601,68 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_update_bits_check); void regmap_async_complete_cb(struct regmap_async *async, int ret) { struct regmap *map = async->map; bool wake; spin_lock(&map->async_lock); list_del(&async->list); wake = list_empty(&map->async_list); if (ret != 0) map->async_ret = ret; spin_unlock(&map->async_lock); schedule_work(&async->cleanup); if (wake) wake_up(&map->async_waitq); } EXPORT_SYMBOL_GPL(regmap_async_complete_cb); static int regmap_async_is_done(struct regmap *map) { unsigned long flags; int ret; spin_lock_irqsave(&map->async_lock, flags); ret = list_empty(&map->async_list); spin_unlock_irqrestore(&map->async_lock, flags); return ret; } /** * regmap_async_complete: Ensure all asynchronous I/O has completed. * * @map: Map to operate on. * * Blocks until any pending asynchronous I/O has completed. Returns * an error code for any failed I/O operations. */ int regmap_async_complete(struct regmap *map) { unsigned long flags; int ret; /* Nothing to do with no async support */ if (!map->bus->async_write) return 0; wait_event(map->async_waitq, regmap_async_is_done(map)); spin_lock_irqsave(&map->async_lock, flags); ret = map->async_ret; map->async_ret = 0; spin_unlock_irqrestore(&map->async_lock, flags); return ret; } EXPORT_SYMBOL_GPL(regmap_async_complete); /** * regmap_register_patch: Register and apply register updates to be applied * on device initialistion Loading
include/linux/regmap.h +28 −0 Original line number Diff line number Diff line Loading @@ -235,14 +235,21 @@ struct regmap_range_cfg { unsigned int window_len; }; struct regmap_async; typedef int (*regmap_hw_write)(void *context, const void *data, size_t count); typedef int (*regmap_hw_gather_write)(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len); typedef int (*regmap_hw_async_write)(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len, struct regmap_async *async); typedef int (*regmap_hw_read)(void *context, const void *reg_buf, size_t reg_size, void *val_buf, size_t val_size); typedef struct regmap_async *(*regmap_hw_async_alloc)(void); typedef void (*regmap_hw_free_context)(void *context); /** Loading @@ -255,8 +262,11 @@ typedef void (*regmap_hw_free_context)(void *context); * @write: Write operation. * @gather_write: Write operation with split register/value, return -ENOTSUPP * if not implemented on a given device. * @async_write: Write operation which completes asynchronously, optional and * must serialise with respect to non-async I/O. * @read: Read operation. Data is returned in the buffer used to transmit * data. * @async_alloc: Allocate a regmap_async() structure. * @read_flag_mask: Mask to be set in the top byte of the register when doing * a read. * @reg_format_endian_default: Default endianness for formatted register Loading @@ -265,13 +275,16 @@ typedef void (*regmap_hw_free_context)(void *context); * @val_format_endian_default: Default endianness for formatted register * values. Used when the regmap_config specifies DEFAULT. If this is * DEFAULT, BIG is assumed. * @async_size: Size of struct used for async work. */ struct regmap_bus { bool fast_io; regmap_hw_write write; regmap_hw_gather_write gather_write; regmap_hw_async_write async_write; regmap_hw_read read; regmap_hw_free_context free_context; regmap_hw_async_alloc async_alloc; u8 read_flag_mask; enum regmap_endian reg_format_endian_default; enum regmap_endian val_format_endian_default; Loading Loading @@ -310,6 +323,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count); int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val); int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len); Loading @@ -321,6 +336,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); int regmap_get_val_bytes(struct regmap *map); int regmap_async_complete(struct regmap *map); int regcache_sync(struct regmap *map); int regcache_sync_region(struct regmap *map, unsigned int min, Loading Loading @@ -422,6 +438,13 @@ static inline int regmap_raw_write(struct regmap *map, unsigned int reg, return -EINVAL; } static inline int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { WARN_ONCE(1, "regmap API is disabled"); return -EINVAL; } static inline int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count) { Loading Loading @@ -500,6 +523,11 @@ static inline void regcache_mark_dirty(struct regmap *map) WARN_ONCE(1, "regmap API is disabled"); } static inline void regmap_async_complete(struct regmap *map) { WARN_ONCE(1, "regmap API is disabled"); } static inline int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs) Loading