Commit 5062ecdb authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'regmap-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap

Pull regmap updates from Mark Brown:
 "Quite a few new features for regmap this time, mostly expanding things
  around the edges of the existing functionality to cover more devices
  rather than thinsg with wide applicability:

   - Support for offload of the update_bits() operation to hardware
     where devices implement bit level access.
   - Support for a few extra operations that need scratch buffers on
     fast_io devices where we can't sleep.
   - Expanded the feature set of regmap_irq to cope with some extra
     register layouts.
   - Cleanups to the debugfs code"

* tag 'regmap-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: Allow installing custom reg_update_bits function
  regmap: debugfs: simplify regmap_reg_ranges_read_file() slightly
  regmap: debugfs: use memcpy instead of snprintf
  regmap: debugfs: use snprintf return value in regmap_reg_ranges_read_file()
  regmap: Add generic macro to define regmap_irq
  regmap: debugfs: Remove scratch buffer for register length calculation
  regmap: irq: add ack_invert flag for chips using cleared bits as ack
  regmap: irq: add support for chips who have separate unmask registers
  regmap: Allocate buffers with GFP_ATOMIC when fast_io == true
parents e86328c4 d4a1a317
...@@ -59,6 +59,7 @@ struct regmap { ...@@ -59,6 +59,7 @@ struct regmap {
regmap_lock lock; regmap_lock lock;
regmap_unlock unlock; regmap_unlock unlock;
void *lock_arg; /* This is passed to lock/unlock functions */ void *lock_arg; /* This is passed to lock/unlock functions */
gfp_t alloc_flags;
struct device *dev; /* Device we do I/O on */ struct device *dev; /* Device we do I/O on */
void *work_buf; /* Scratch buffer used to format I/O */ void *work_buf; /* Scratch buffer used to format I/O */
...@@ -98,6 +99,8 @@ struct regmap { ...@@ -98,6 +99,8 @@ struct regmap {
int (*reg_read)(void *context, unsigned int reg, unsigned int *val); int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg, unsigned int val); int (*reg_write)(void *context, unsigned int reg, unsigned int val);
int (*reg_update_bits)(void *context, unsigned int reg,
unsigned int mask, unsigned int val);
bool defer_caching; bool defer_caching;
......
...@@ -30,7 +30,7 @@ static LIST_HEAD(regmap_debugfs_early_list); ...@@ -30,7 +30,7 @@ static LIST_HEAD(regmap_debugfs_early_list);
static DEFINE_MUTEX(regmap_debugfs_early_lock); static DEFINE_MUTEX(regmap_debugfs_early_lock);
/* Calculate the length of a fixed format */ /* Calculate the length of a fixed format */
static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size) static size_t regmap_calc_reg_len(int max_val)
{ {
return snprintf(NULL, 0, "%x", max_val); return snprintf(NULL, 0, "%x", max_val);
} }
...@@ -173,8 +173,7 @@ static inline void regmap_calc_tot_len(struct regmap *map, ...@@ -173,8 +173,7 @@ static inline void regmap_calc_tot_len(struct regmap *map,
{ {
/* Calculate the length of a fixed format */ /* Calculate the length of a fixed format */
if (!map->debugfs_tot_len) { if (!map->debugfs_tot_len) {
map->debugfs_reg_len = regmap_calc_reg_len(map->max_register, map->debugfs_reg_len = regmap_calc_reg_len(map->max_register),
buf, count);
map->debugfs_val_len = 2 * map->format.val_bytes; map->debugfs_val_len = 2 * map->format.val_bytes;
map->debugfs_tot_len = map->debugfs_reg_len + map->debugfs_tot_len = map->debugfs_reg_len +
map->debugfs_val_len + 3; /* : \n */ map->debugfs_val_len + 3; /* : \n */
...@@ -338,6 +337,7 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file, ...@@ -338,6 +337,7 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
char *buf; char *buf;
char *entry; char *entry;
int ret; int ret;
unsigned entry_len;
if (*ppos < 0 || !count) if (*ppos < 0 || !count)
return -EINVAL; return -EINVAL;
...@@ -365,18 +365,15 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file, ...@@ -365,18 +365,15 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
p = 0; p = 0;
mutex_lock(&map->cache_lock); mutex_lock(&map->cache_lock);
list_for_each_entry(c, &map->debugfs_off_cache, list) { list_for_each_entry(c, &map->debugfs_off_cache, list) {
snprintf(entry, PAGE_SIZE, "%x-%x", entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n",
c->base_reg, c->max_reg); c->base_reg, c->max_reg);
if (p >= *ppos) { if (p >= *ppos) {
if (buf_pos + 1 + strlen(entry) > count) if (buf_pos + entry_len > count)
break; break;
snprintf(buf + buf_pos, count - buf_pos, memcpy(buf + buf_pos, entry, entry_len);
"%s", entry); buf_pos += entry_len;
buf_pos += strlen(entry);
buf[buf_pos] = '\n';
buf_pos++;
} }
p += strlen(entry) + 1; p += entry_len;
} }
mutex_unlock(&map->cache_lock); mutex_unlock(&map->cache_lock);
...@@ -420,7 +417,7 @@ static ssize_t regmap_access_read_file(struct file *file, ...@@ -420,7 +417,7 @@ static ssize_t regmap_access_read_file(struct file *file,
return -ENOMEM; return -ENOMEM;
/* Calculate the length of a fixed format */ /* Calculate the length of a fixed format */
reg_len = regmap_calc_reg_len(map->max_register, buf, count); reg_len = regmap_calc_reg_len(map->max_register);
tot_len = reg_len + 10; /* ': R W V P\n' */ tot_len = reg_len + 10; /* ': R W V P\n' */
for (i = 0; i <= map->max_register; i += map->reg_stride) { for (i = 0; i <= map->max_register; i += map->reg_stride) {
......
...@@ -63,6 +63,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -63,6 +63,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
struct regmap *map = d->map; struct regmap *map = d->map;
int i, ret; int i, ret;
u32 reg; u32 reg;
u32 unmask_offset;
if (d->chip->runtime_pm) { if (d->chip->runtime_pm) {
ret = pm_runtime_get_sync(map->dev); ret = pm_runtime_get_sync(map->dev);
...@@ -79,12 +80,28 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -79,12 +80,28 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
for (i = 0; i < d->chip->num_regs; i++) { for (i = 0; i < d->chip->num_regs; i++) {
reg = d->chip->mask_base + reg = d->chip->mask_base +
(i * map->reg_stride * d->irq_reg_stride); (i * map->reg_stride * d->irq_reg_stride);
if (d->chip->mask_invert) if (d->chip->mask_invert) {
ret = regmap_update_bits(d->map, reg, ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], ~d->mask_buf[i]); d->mask_buf_def[i], ~d->mask_buf[i]);
else } else if (d->chip->unmask_base) {
/* set mask with mask_base register */
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret < 0)
dev_err(d->map->dev,
"Failed to sync unmasks in %x\n",
reg);
unmask_offset = d->chip->unmask_base -
d->chip->mask_base;
/* clear mask with unmask_base register */
ret = regmap_update_bits(d->map,
reg + unmask_offset,
d->mask_buf_def[i],
d->mask_buf[i]);
} else {
ret = regmap_update_bits(d->map, reg, ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], d->mask_buf[i]); d->mask_buf_def[i], d->mask_buf[i]);
}
if (ret != 0) if (ret != 0)
dev_err(d->map->dev, "Failed to sync masks in %x\n", dev_err(d->map->dev, "Failed to sync masks in %x\n",
reg); reg);
...@@ -116,6 +133,10 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -116,6 +133,10 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) { if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) {
reg = d->chip->ack_base + reg = d->chip->ack_base +
(i * map->reg_stride * d->irq_reg_stride); (i * map->reg_stride * d->irq_reg_stride);
/* some chips ack by write 0 */
if (d->chip->ack_invert)
ret = regmap_write(map, reg, ~d->mask_buf[i]);
else
ret = regmap_write(map, reg, d->mask_buf[i]); ret = regmap_write(map, reg, d->mask_buf[i]);
if (ret != 0) if (ret != 0)
dev_err(d->map->dev, "Failed to ack 0x%x: %d\n", dev_err(d->map->dev, "Failed to ack 0x%x: %d\n",
...@@ -339,6 +360,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -339,6 +360,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
int i; int i;
int ret = -ENOMEM; int ret = -ENOMEM;
u32 reg; u32 reg;
u32 unmask_offset;
if (chip->num_regs <= 0) if (chip->num_regs <= 0)
return -EINVAL; return -EINVAL;
...@@ -420,7 +442,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -420,7 +442,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
if (chip->mask_invert) if (chip->mask_invert)
ret = regmap_update_bits(map, reg, ret = regmap_update_bits(map, reg,
d->mask_buf[i], ~d->mask_buf[i]); d->mask_buf[i], ~d->mask_buf[i]);
else else if (d->chip->unmask_base) {
unmask_offset = d->chip->unmask_base -
d->chip->mask_base;
ret = regmap_update_bits(d->map,
reg + unmask_offset,
d->mask_buf[i],
d->mask_buf[i]);
} else
ret = regmap_update_bits(map, reg, ret = regmap_update_bits(map, reg,
d->mask_buf[i], d->mask_buf[i]); d->mask_buf[i], d->mask_buf[i]);
if (ret != 0) { if (ret != 0) {
...@@ -445,6 +474,10 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -445,6 +474,10 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) {
reg = chip->ack_base + reg = chip->ack_base +
(i * map->reg_stride * d->irq_reg_stride); (i * map->reg_stride * d->irq_reg_stride);
if (chip->ack_invert)
ret = regmap_write(map, reg,
~(d->status_buf[i] & d->mask_buf[i]));
else
ret = regmap_write(map, reg, ret = regmap_write(map, reg,
d->status_buf[i] & d->mask_buf[i]); d->status_buf[i] & d->mask_buf[i]);
if (ret != 0) { if (ret != 0) {
......
...@@ -561,6 +561,16 @@ struct regmap *__regmap_init(struct device *dev, ...@@ -561,6 +561,16 @@ struct regmap *__regmap_init(struct device *dev,
} }
map->lock_arg = map; map->lock_arg = map;
} }
/*
* When we write in fast-paths with regmap_bulk_write() don't allocate
* scratch buffers with sleeping allocations.
*/
if ((bus && bus->fast_io) || config->fast_io)
map->alloc_flags = GFP_ATOMIC;
else
map->alloc_flags = GFP_KERNEL;
map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
map->format.pad_bytes = config->pad_bits / 8; map->format.pad_bytes = config->pad_bits / 8;
map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
...@@ -619,6 +629,7 @@ struct regmap *__regmap_init(struct device *dev, ...@@ -619,6 +629,7 @@ struct regmap *__regmap_init(struct device *dev,
goto skip_format_initialization; goto skip_format_initialization;
} else { } else {
map->reg_read = _regmap_bus_read; map->reg_read = _regmap_bus_read;
map->reg_update_bits = bus->reg_update_bits;
} }
reg_endian = regmap_get_reg_endian(bus, config); reg_endian = regmap_get_reg_endian(bus, config);
...@@ -1786,7 +1797,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, ...@@ -1786,7 +1797,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
if (!val_count) if (!val_count)
return -EINVAL; return -EINVAL;
wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL); wval = kmemdup(val, val_count * val_bytes, map->alloc_flags);
if (!wval) { if (!wval) {
dev_err(map->dev, "Error in memory allocation\n"); dev_err(map->dev, "Error in memory allocation\n");
return -ENOMEM; return -ENOMEM;
...@@ -2509,6 +2520,14 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, ...@@ -2509,6 +2520,14 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
int ret; int ret;
unsigned int tmp, orig; unsigned int tmp, orig;
if (change)
*change = false;
if (regmap_volatile(map, reg) && map->reg_update_bits) {
ret = map->reg_update_bits(map->bus_context, reg, mask, val);
if (ret == 0 && change)
*change = true;
} else {
ret = _regmap_read(map, reg, &orig); ret = _regmap_read(map, reg, &orig);
if (ret != 0) if (ret != 0)
return ret; return ret;
...@@ -2518,11 +2537,9 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, ...@@ -2518,11 +2537,9 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
if (force_write || (tmp != orig)) { if (force_write || (tmp != orig)) {
ret = _regmap_write(map, reg, tmp); ret = _regmap_write(map, reg, tmp);
if (change) if (ret == 0 && change)
*change = true; *change = true;
} else { }
if (change)
*change = false;
} }
return ret; return ret;
......
...@@ -296,6 +296,8 @@ typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg, ...@@ -296,6 +296,8 @@ typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg,
unsigned int *val); unsigned int *val);
typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg, typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg,
unsigned int val); unsigned int val);
typedef int (*regmap_hw_reg_update_bits)(void *context, unsigned int reg,
unsigned int mask, unsigned int val);
typedef struct regmap_async *(*regmap_hw_async_alloc)(void); typedef struct regmap_async *(*regmap_hw_async_alloc)(void);
typedef void (*regmap_hw_free_context)(void *context); typedef void (*regmap_hw_free_context)(void *context);
...@@ -335,6 +337,7 @@ struct regmap_bus { ...@@ -335,6 +337,7 @@ struct regmap_bus {
regmap_hw_gather_write gather_write; regmap_hw_gather_write gather_write;
regmap_hw_async_write async_write; regmap_hw_async_write async_write;
regmap_hw_reg_write reg_write; regmap_hw_reg_write reg_write;
regmap_hw_reg_update_bits reg_update_bits;
regmap_hw_read read; regmap_hw_read read;
regmap_hw_reg_read reg_read; regmap_hw_reg_read reg_read;
regmap_hw_free_context free_context; regmap_hw_free_context free_context;
...@@ -791,6 +794,9 @@ struct regmap_irq { ...@@ -791,6 +794,9 @@ struct regmap_irq {
unsigned int mask; unsigned int mask;
}; };
#define REGMAP_IRQ_REG(_irq, _off, _mask) \
[_irq] = { .reg_offset = (_off), .mask = (_mask) }
/** /**
* Description of a generic regmap irq_chip. This is not intended to * Description of a generic regmap irq_chip. This is not intended to
* handle every possible interrupt controller, but it should handle a * handle every possible interrupt controller, but it should handle a
...@@ -800,6 +806,8 @@ struct regmap_irq { ...@@ -800,6 +806,8 @@ struct regmap_irq {
* *
* @status_base: Base status register address. * @status_base: Base status register address.
* @mask_base: Base mask register address. * @mask_base: Base mask register address.
* @unmask_base: Base unmask register address. for chips who have
* separate mask and unmask registers
* @ack_base: Base ack address. If zero then the chip is clear on read. * @ack_base: Base ack address. If zero then the chip is clear on read.
* Using zero value is possible with @use_ack bit. * Using zero value is possible with @use_ack bit.
* @wake_base: Base address for wake enables. If zero unsupported. * @wake_base: Base address for wake enables. If zero unsupported.
...@@ -807,6 +815,7 @@ struct regmap_irq { ...@@ -807,6 +815,7 @@ struct regmap_irq {
* @init_ack_masked: Ack all masked interrupts once during initalization. * @init_ack_masked: Ack all masked interrupts once during initalization.
* @mask_invert: Inverted mask register: cleared bits are masked out. * @mask_invert: Inverted mask register: cleared bits are masked out.
* @use_ack: Use @ack register even if it is zero. * @use_ack: Use @ack register even if it is zero.
* @ack_invert: Inverted ack register: cleared bits for ack.
* @wake_invert: Inverted wake register: cleared bits are wake enabled. * @wake_invert: Inverted wake register: cleared bits are wake enabled.
* @runtime_pm: Hold a runtime PM lock on the device when accessing it. * @runtime_pm: Hold a runtime PM lock on the device when accessing it.
* *
...@@ -820,12 +829,14 @@ struct regmap_irq_chip { ...@@ -820,12 +829,14 @@ struct regmap_irq_chip {
unsigned int status_base; unsigned int status_base;
unsigned int mask_base; unsigned int mask_base;
unsigned int unmask_base;
unsigned int ack_base; unsigned int ack_base;
unsigned int wake_base; unsigned int wake_base;
unsigned int irq_reg_stride; unsigned int irq_reg_stride;
bool init_ack_masked:1; bool init_ack_masked:1;
bool mask_invert:1; bool mask_invert:1;
bool use_ack:1; bool use_ack:1;
bool ack_invert:1;
bool wake_invert:1; bool wake_invert:1;
bool runtime_pm:1; bool runtime_pm:1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment