Commit 8243b7f5 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull regmap updates from Mark Brown:
 "A quiet release for regmap, some cleanups, fixes and:

   - Improved node coalescing for rbtree, reducing memory usage and
     improving performance during syncs.
   - Support for registering multiple register patches.
   - A quirk for handling interrupts that need to be clear when masked
     in regmap-irq"

* tag 'regmap-v3.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: rbtree: Make cache_present bitmap per node
  regmap: rbtree: Reduce number of nodes, take 2
  regmap: rbtree: Simplify adjacent node look-up
  regmap: debugfs: Fix continued read from registers file
  regcache-rbtree: Fix reg_stride != 1
  regmap: Allow multiple patches to be registered
  regmap: regcache: allow read-only regs to be cached
  regmap: fix regcache_reg_present() for empty cache
  regmap: core: allow a virtual range to cover its own data window
  regmap: irq: document mask/wake_invert flags
  regmap: irq: make flags bool and put them in a bitfield
  regmap: irq: Allow to acknowledge masked interrupts during initialization
  regmap: Provide __acquires/__releases annotations
parents fc6d0b03 365c9ee0
...@@ -128,9 +128,6 @@ struct regmap { ...@@ -128,9 +128,6 @@ struct regmap {
void *cache; void *cache;
u32 cache_dirty; u32 cache_dirty;
unsigned long *cache_present;
unsigned int cache_present_nbits;
struct reg_default *patch; struct reg_default *patch;
int patch_regs; int patch_regs;
...@@ -203,6 +200,7 @@ int regcache_write(struct regmap *map, ...@@ -203,6 +200,7 @@ int regcache_write(struct regmap *map,
unsigned int reg, unsigned int value); unsigned int reg, unsigned int value);
int regcache_sync(struct regmap *map); int regcache_sync(struct regmap *map);
int regcache_sync_block(struct regmap *map, void *block, int regcache_sync_block(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base, unsigned int start, unsigned int block_base, unsigned int start,
unsigned int end); unsigned int end);
...@@ -218,16 +216,6 @@ unsigned int regcache_get_val(struct regmap *map, const void *base, ...@@ -218,16 +216,6 @@ unsigned int regcache_get_val(struct regmap *map, const void *base,
bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
unsigned int val); unsigned int val);
int regcache_lookup_reg(struct regmap *map, unsigned int reg); int regcache_lookup_reg(struct regmap *map, unsigned int reg);
int regcache_set_reg_present(struct regmap *map, unsigned int reg);
static inline bool regcache_reg_present(struct regmap *map, unsigned int reg)
{
if (!map->cache_present)
return true;
if (reg > map->cache_present_nbits)
return false;
return map->cache_present[BIT_WORD(reg)] & BIT_MASK(reg);
}
int _regmap_raw_write(struct regmap *map, unsigned int reg, int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool async); const void *val, size_t val_len, bool async);
......
...@@ -29,6 +29,8 @@ struct regcache_rbtree_node { ...@@ -29,6 +29,8 @@ struct regcache_rbtree_node {
unsigned int base_reg; unsigned int base_reg;
/* block of adjacent registers */ /* block of adjacent registers */
void *block; void *block;
/* Which registers are present */
long *cache_present;
/* number of registers available in the block */ /* number of registers available in the block */
unsigned int blklen; unsigned int blklen;
} __attribute__ ((packed)); } __attribute__ ((packed));
...@@ -57,6 +59,7 @@ static void regcache_rbtree_set_register(struct regmap *map, ...@@ -57,6 +59,7 @@ static void regcache_rbtree_set_register(struct regmap *map,
struct regcache_rbtree_node *rbnode, struct regcache_rbtree_node *rbnode,
unsigned int idx, unsigned int val) unsigned int idx, unsigned int val)
{ {
set_bit(idx, rbnode->cache_present);
regcache_set_val(map, rbnode->block, idx, val); regcache_set_val(map, rbnode->block, idx, val);
} }
...@@ -146,13 +149,13 @@ static int rbtree_show(struct seq_file *s, void *ignored) ...@@ -146,13 +149,13 @@ static int rbtree_show(struct seq_file *s, void *ignored)
map->lock(map->lock_arg); map->lock(map->lock_arg);
mem_size = sizeof(*rbtree_ctx); mem_size = sizeof(*rbtree_ctx);
mem_size += BITS_TO_LONGS(map->cache_present_nbits) * sizeof(long);
for (node = rb_first(&rbtree_ctx->root); node != NULL; for (node = rb_first(&rbtree_ctx->root); node != NULL;
node = rb_next(node)) { node = rb_next(node)) {
n = container_of(node, struct regcache_rbtree_node, node); n = container_of(node, struct regcache_rbtree_node, node);
mem_size += sizeof(*n); mem_size += sizeof(*n);
mem_size += (n->blklen * map->cache_word_size); mem_size += (n->blklen * map->cache_word_size);
mem_size += BITS_TO_LONGS(n->blklen) * sizeof(long);
regcache_rbtree_get_base_top_reg(map, n, &base, &top); regcache_rbtree_get_base_top_reg(map, n, &base, &top);
this_registers = ((top - base) / map->reg_stride) + 1; this_registers = ((top - base) / map->reg_stride) + 1;
...@@ -245,6 +248,7 @@ static int regcache_rbtree_exit(struct regmap *map) ...@@ -245,6 +248,7 @@ static int regcache_rbtree_exit(struct regmap *map)
rbtree_node = rb_entry(next, struct regcache_rbtree_node, node); rbtree_node = rb_entry(next, struct regcache_rbtree_node, node);
next = rb_next(&rbtree_node->node); next = rb_next(&rbtree_node->node);
rb_erase(&rbtree_node->node, &rbtree_ctx->root); rb_erase(&rbtree_node->node, &rbtree_ctx->root);
kfree(rbtree_node->cache_present);
kfree(rbtree_node->block); kfree(rbtree_node->block);
kfree(rbtree_node); kfree(rbtree_node);
} }
...@@ -265,7 +269,7 @@ static int regcache_rbtree_read(struct regmap *map, ...@@ -265,7 +269,7 @@ static int regcache_rbtree_read(struct regmap *map,
rbnode = regcache_rbtree_lookup(map, reg); rbnode = regcache_rbtree_lookup(map, reg);
if (rbnode) { if (rbnode) {
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
if (!regcache_reg_present(map, reg)) if (!test_bit(reg_tmp, rbnode->cache_present))
return -ENOENT; return -ENOENT;
*value = regcache_rbtree_get_register(map, rbnode, reg_tmp); *value = regcache_rbtree_get_register(map, rbnode, reg_tmp);
} else { } else {
...@@ -278,27 +282,45 @@ static int regcache_rbtree_read(struct regmap *map, ...@@ -278,27 +282,45 @@ static int regcache_rbtree_read(struct regmap *map,
static int regcache_rbtree_insert_to_block(struct regmap *map, static int regcache_rbtree_insert_to_block(struct regmap *map,
struct regcache_rbtree_node *rbnode, struct regcache_rbtree_node *rbnode,
unsigned int pos, unsigned int reg, unsigned int base_reg,
unsigned int top_reg,
unsigned int reg,
unsigned int value) unsigned int value)
{ {
unsigned int blklen;
unsigned int pos, offset;
unsigned long *present;
u8 *blk; u8 *blk;
blklen = (top_reg - base_reg) / map->reg_stride + 1;
pos = (reg - base_reg) / map->reg_stride;
offset = (rbnode->base_reg - base_reg) / map->reg_stride;
blk = krealloc(rbnode->block, blk = krealloc(rbnode->block,
(rbnode->blklen + 1) * map->cache_word_size, blklen * map->cache_word_size,
GFP_KERNEL); GFP_KERNEL);
if (!blk) if (!blk)
return -ENOMEM; return -ENOMEM;
present = krealloc(rbnode->cache_present,
BITS_TO_LONGS(blklen) * sizeof(*present), GFP_KERNEL);
if (!present) {
kfree(blk);
return -ENOMEM;
}
/* insert the register value in the correct place in the rbnode block */ /* insert the register value in the correct place in the rbnode block */
memmove(blk + (pos + 1) * map->cache_word_size, if (pos == 0) {
blk + pos * map->cache_word_size, memmove(blk + offset * map->cache_word_size,
(rbnode->blklen - pos) * map->cache_word_size); blk, rbnode->blklen * map->cache_word_size);
bitmap_shift_right(present, present, offset, blklen);
}
/* update the rbnode block, its size and the base register */ /* update the rbnode block, its size and the base register */
rbnode->block = blk; rbnode->block = blk;
rbnode->blklen++; rbnode->blklen = blklen;
if (!pos) rbnode->base_reg = base_reg;
rbnode->base_reg = reg; rbnode->cache_present = present;
regcache_rbtree_set_register(map, rbnode, pos, value); regcache_rbtree_set_register(map, rbnode, pos, value);
return 0; return 0;
...@@ -325,8 +347,8 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg) ...@@ -325,8 +347,8 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
if (i != map->rd_table->n_yes_ranges) { if (i != map->rd_table->n_yes_ranges) {
range = &map->rd_table->yes_ranges[i]; range = &map->rd_table->yes_ranges[i];
rbnode->blklen = range->range_max - range->range_min rbnode->blklen = (range->range_max - range->range_min) /
+ 1; map->reg_stride + 1;
rbnode->base_reg = range->range_min; rbnode->base_reg = range->range_min;
} }
} }
...@@ -338,12 +360,21 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg) ...@@ -338,12 +360,21 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
GFP_KERNEL); GFP_KERNEL);
if (!rbnode->block) { if (!rbnode->block)
kfree(rbnode); goto err_free;
return NULL;
} rbnode->cache_present = kzalloc(BITS_TO_LONGS(rbnode->blklen) *
sizeof(*rbnode->cache_present), GFP_KERNEL);
if (!rbnode->cache_present)
goto err_free_block;
return rbnode; return rbnode;
err_free_block:
kfree(rbnode->block);
err_free:
kfree(rbnode);
return NULL;
} }
static int regcache_rbtree_write(struct regmap *map, unsigned int reg, static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
...@@ -353,15 +384,9 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, ...@@ -353,15 +384,9 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
struct regcache_rbtree_node *rbnode, *rbnode_tmp; struct regcache_rbtree_node *rbnode, *rbnode_tmp;
struct rb_node *node; struct rb_node *node;
unsigned int reg_tmp; unsigned int reg_tmp;
unsigned int pos;
int i;
int ret; int ret;
rbtree_ctx = map->cache; rbtree_ctx = map->cache;
/* update the reg_present bitmap, make space if necessary */
ret = regcache_set_reg_present(map, reg);
if (ret < 0)
return ret;
/* if we can't locate it in the cached rbnode we'll have /* if we can't locate it in the cached rbnode we'll have
* to traverse the rbtree looking for it. * to traverse the rbtree looking for it.
...@@ -371,31 +396,44 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, ...@@ -371,31 +396,44 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
regcache_rbtree_set_register(map, rbnode, reg_tmp, value); regcache_rbtree_set_register(map, rbnode, reg_tmp, value);
} else { } else {
unsigned int base_reg, top_reg;
unsigned int new_base_reg, new_top_reg;
unsigned int min, max;
unsigned int max_dist;
max_dist = map->reg_stride * sizeof(*rbnode_tmp) /
map->cache_word_size;
if (reg < max_dist)
min = 0;
else
min = reg - max_dist;
max = reg + max_dist;
/* look for an adjacent register to the one we are about to add */ /* look for an adjacent register to the one we are about to add */
for (node = rb_first(&rbtree_ctx->root); node; for (node = rb_first(&rbtree_ctx->root); node;
node = rb_next(node)) { node = rb_next(node)) {
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
node); node);
for (i = 0; i < rbnode_tmp->blklen; i++) {
reg_tmp = rbnode_tmp->base_reg + regcache_rbtree_get_base_top_reg(map, rbnode_tmp,
(i * map->reg_stride); &base_reg, &top_reg);
if (abs(reg_tmp - reg) != map->reg_stride)
if (base_reg <= max && top_reg >= min) {
new_base_reg = min(reg, base_reg);
new_top_reg = max(reg, top_reg);
} else {
continue; continue;
/* decide where in the block to place our register */ }
if (reg_tmp + map->reg_stride == reg)
pos = i + 1; ret = regcache_rbtree_insert_to_block(map, rbnode_tmp,
else new_base_reg,
pos = i; new_top_reg, reg,
ret = regcache_rbtree_insert_to_block(map,
rbnode_tmp,
pos, reg,
value); value);
if (ret) if (ret)
return ret; return ret;
rbtree_ctx->cached_rbnode = rbnode_tmp; rbtree_ctx->cached_rbnode = rbnode_tmp;
return 0; return 0;
} }
}
/* We did not manage to find a place to insert it in /* We did not manage to find a place to insert it in
* an existing block so create a new rbnode. * an existing block so create a new rbnode.
...@@ -418,30 +456,34 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, ...@@ -418,30 +456,34 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
struct regcache_rbtree_ctx *rbtree_ctx; struct regcache_rbtree_ctx *rbtree_ctx;
struct rb_node *node; struct rb_node *node;
struct regcache_rbtree_node *rbnode; struct regcache_rbtree_node *rbnode;
unsigned int base_reg, top_reg;
unsigned int start, end;
int ret; int ret;
int base, end;
rbtree_ctx = map->cache; rbtree_ctx = map->cache;
for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
rbnode = rb_entry(node, struct regcache_rbtree_node, node); rbnode = rb_entry(node, struct regcache_rbtree_node, node);
if (rbnode->base_reg > max) regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
&top_reg);
if (base_reg > max)
break; break;
if (rbnode->base_reg + rbnode->blklen < min) if (top_reg < min)
continue; continue;
if (min > rbnode->base_reg) if (min > base_reg)
base = min - rbnode->base_reg; start = (min - base_reg) / map->reg_stride;
else else
base = 0; start = 0;
if (max < rbnode->base_reg + rbnode->blklen) if (max < top_reg)
end = max - rbnode->base_reg + 1; end = (max - base_reg) / map->reg_stride + 1;
else else
end = rbnode->blklen; end = rbnode->blklen;
ret = regcache_sync_block(map, rbnode->block, rbnode->base_reg, ret = regcache_sync_block(map, rbnode->block,
base, end); rbnode->cache_present,
rbnode->base_reg, start, end);
if (ret != 0) if (ret != 0)
return ret; return ret;
} }
...@@ -449,6 +491,42 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, ...@@ -449,6 +491,42 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
return regmap_async_complete(map); return regmap_async_complete(map);
} }
static int regcache_rbtree_drop(struct regmap *map, unsigned int min,
unsigned int max)
{
struct regcache_rbtree_ctx *rbtree_ctx;
struct regcache_rbtree_node *rbnode;
struct rb_node *node;
unsigned int base_reg, top_reg;
unsigned int start, end;
rbtree_ctx = map->cache;
for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
rbnode = rb_entry(node, struct regcache_rbtree_node, node);
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
&top_reg);
if (base_reg > max)
break;
if (top_reg < min)
continue;
if (min > base_reg)
start = (min - base_reg) / map->reg_stride;
else
start = 0;
if (max < top_reg)
end = (max - base_reg) / map->reg_stride + 1;
else
end = rbnode->blklen;
bitmap_clear(rbnode->cache_present, start, end - start);
}
return 0;
}
struct regcache_ops regcache_rbtree_ops = { struct regcache_ops regcache_rbtree_ops = {
.type = REGCACHE_RBTREE, .type = REGCACHE_RBTREE,
.name = "rbtree", .name = "rbtree",
...@@ -456,5 +534,6 @@ struct regcache_ops regcache_rbtree_ops = { ...@@ -456,5 +534,6 @@ struct regcache_ops regcache_rbtree_ops = {
.exit = regcache_rbtree_exit, .exit = regcache_rbtree_exit,
.read = regcache_rbtree_read, .read = regcache_rbtree_read,
.write = regcache_rbtree_write, .write = regcache_rbtree_write,
.sync = regcache_rbtree_sync .sync = regcache_rbtree_sync,
.drop = regcache_rbtree_drop,
}; };
...@@ -121,8 +121,6 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) ...@@ -121,8 +121,6 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
map->reg_defaults_raw = config->reg_defaults_raw; map->reg_defaults_raw = config->reg_defaults_raw;
map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8); map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8);
map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw; map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw;
map->cache_present = NULL;
map->cache_present_nbits = 0;
map->cache = NULL; map->cache = NULL;
map->cache_ops = cache_types[i]; map->cache_ops = cache_types[i];
...@@ -181,7 +179,6 @@ void regcache_exit(struct regmap *map) ...@@ -181,7 +179,6 @@ void regcache_exit(struct regmap *map)
BUG_ON(!map->cache_ops); BUG_ON(!map->cache_ops);
kfree(map->cache_present);
kfree(map->reg_defaults); kfree(map->reg_defaults);
if (map->cache_free) if (map->cache_free)
kfree(map->reg_defaults_raw); kfree(map->reg_defaults_raw);
...@@ -241,9 +238,6 @@ int regcache_write(struct regmap *map, ...@@ -241,9 +238,6 @@ int regcache_write(struct regmap *map,
BUG_ON(!map->cache_ops); BUG_ON(!map->cache_ops);
if (!regmap_writeable(map, reg))
return -EIO;
if (!regmap_volatile(map, reg)) if (!regmap_volatile(map, reg))
return map->cache_ops->write(map, reg, value); return map->cache_ops->write(map, reg, value);
...@@ -410,21 +404,15 @@ EXPORT_SYMBOL_GPL(regcache_sync_region); ...@@ -410,21 +404,15 @@ EXPORT_SYMBOL_GPL(regcache_sync_region);
int regcache_drop_region(struct regmap *map, unsigned int min, int regcache_drop_region(struct regmap *map, unsigned int min,
unsigned int max) unsigned int max)
{ {
unsigned int reg;
int ret = 0; int ret = 0;
if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop)) if (!map->cache_ops || !map->cache_ops->drop)
return -EINVAL; return -EINVAL;
map->lock(map->lock_arg); map->lock(map->lock_arg);
trace_regcache_drop_region(map->dev, min, max); trace_regcache_drop_region(map->dev, min, max);
if (map->cache_present)
for (reg = min; reg < max + 1; reg++)
clear_bit(reg, map->cache_present);
if (map->cache_ops && map->cache_ops->drop)
ret = map->cache_ops->drop(map, min, max); ret = map->cache_ops->drop(map, min, max);
map->unlock(map->lock_arg); map->unlock(map->lock_arg);
...@@ -493,42 +481,6 @@ void regcache_cache_bypass(struct regmap *map, bool enable) ...@@ -493,42 +481,6 @@ void regcache_cache_bypass(struct regmap *map, bool enable)
} }
EXPORT_SYMBOL_GPL(regcache_cache_bypass); EXPORT_SYMBOL_GPL(regcache_cache_bypass);
int regcache_set_reg_present(struct regmap *map, unsigned int reg)
{
unsigned long *cache_present;
unsigned int cache_present_size;
unsigned int nregs;
int i;
nregs = reg + 1;
cache_present_size = BITS_TO_LONGS(nregs);
cache_present_size *= sizeof(long);
if (!map->cache_present) {
cache_present = kmalloc(cache_present_size, GFP_KERNEL);
if (!cache_present)
return -ENOMEM;
bitmap_zero(cache_present, nregs);
map->cache_present = cache_present;
map->cache_present_nbits = nregs;
}
if (nregs > map->cache_present_nbits) {
cache_present = krealloc(map->cache_present,
cache_present_size, GFP_KERNEL);
if (!cache_present)
return -ENOMEM;
for (i = 0; i < nregs; i++)
if (i >= map->cache_present_nbits)
clear_bit(i, cache_present);
map->cache_present = cache_present;
map->cache_present_nbits = nregs;
}
set_bit(reg, map->cache_present);
return 0;
}
bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
unsigned int val) unsigned int val)
{ {
...@@ -620,7 +572,16 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg) ...@@ -620,7 +572,16 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg)
return -ENOENT; return -ENOENT;
} }
static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx)
{
if (!cache_present)
return true;
return test_bit(idx, cache_present);
}
static int regcache_sync_block_single(struct regmap *map, void *block, static int regcache_sync_block_single(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base, unsigned int block_base,
unsigned int start, unsigned int end) unsigned int start, unsigned int end)
{ {
...@@ -630,7 +591,7 @@ static int regcache_sync_block_single(struct regmap *map, void *block, ...@@ -630,7 +591,7 @@ static int regcache_sync_block_single(struct regmap *map, void *block,
for (i = start; i < end; i++) { for (i = start; i < end; i++) {
regtmp = block_base + (i * map->reg_stride); regtmp = block_base + (i * map->reg_stride);
if (!regcache_reg_present(map, regtmp)) if (!regcache_reg_present(cache_present, i))
continue; continue;
val = regcache_get_val(map, block, i); val = regcache_get_val(map, block, i);
...@@ -681,6 +642,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, ...@@ -681,6 +642,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,
} }
static int regcache_sync_block_raw(struct regmap *map, void *block, static int regcache_sync_block_raw(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base, unsigned int start, unsigned int block_base, unsigned int start,
unsigned int end) unsigned int end)
{ {
...@@ -693,7 +655,7 @@ static int regcache_sync_block_raw(struct regmap *map, void *block, ...@@ -693,7 +655,7 @@ static int regcache_sync_block_raw(struct regmap *map, void *block,
for (i = start; i < end; i++) { for (i = start; i < end; i++) {
regtmp = block_base + (i * map->reg_stride); regtmp = block_base + (i * map->reg_stride);
if (!regcache_reg_present(map, regtmp)) { if (!regcache_reg_present(cache_present, i)) {
ret = regcache_sync_block_raw_flush(map, &data, ret = regcache_sync_block_raw_flush(map, &data,
base, regtmp); base, regtmp);
if (ret != 0) if (ret != 0)
...@@ -724,13 +686,14 @@ static int regcache_sync_block_raw(struct regmap *map, void *block, ...@@ -724,13 +686,14 @@ static int regcache_sync_block_raw(struct regmap *map, void *block,
} }
int regcache_sync_block(struct regmap *map, void *block, int regcache_sync_block(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base, unsigned int start, unsigned int block_base, unsigned int start,
unsigned int end) unsigned int end)
{ {
if (regmap_can_raw_write(map)) if (regmap_can_raw_write(map))
return regcache_sync_block_raw(map, block, block_base, return regcache_sync_block_raw(map, block, cache_present,
start, end); block_base, start, end);
else else
return regcache_sync_block_single(map, block, block_base, return regcache_sync_block_single(map, block, cache_present,
start, end); block_base, start, end);
} }
...@@ -85,8 +85,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, ...@@ -85,8 +85,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
unsigned int reg_offset; unsigned int reg_offset;
/* Suppress the cache if we're using a subrange */ /* Suppress the cache if we're using a subrange */
if (from) if (base)
return from; return base;
/* /*
* If we don't have a cache build one so we don't have to do a * If we don't have a cache build one so we don't have to do a
......
...@@ -418,6 +418,31 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -418,6 +418,31 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
reg, ret); reg, ret);
goto err_alloc; goto err_alloc;
} }
if (!chip->init_ack_masked)
continue;
/* Ack masked but set interrupts */
reg = chip->status_base +
(i * map->reg_stride * d->irq_reg_stride);
ret = regmap_read(map, reg, &d->status_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n",
ret);
goto err_alloc;
}
if (d->status_buf[i] && chip->ack_base) {
reg = chip->ack_base +
(i * map->reg_stride * d->irq_reg_stride);
ret = regmap_write(map, reg,
d->status_buf[i] & d->mask_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
reg, ret);
goto err_alloc;
}
}
} }
/* Wake is disabled by default */ /* Wake is disabled by default */
......
...@@ -303,6 +303,7 @@ static void regmap_unlock_mutex(void *__map) ...@@ -303,6 +303,7 @@ static void regmap_unlock_mutex(void *__map)
} }
static void regmap_lock_spinlock(void *__map) static void regmap_lock_spinlock(void *__map)
__acquires(&map->spinlock)
{ {
struct regmap *map = __map; struct regmap *map = __map;
unsigned long flags; unsigned long flags;
...@@ -312,6 +313,7 @@ static void regmap_lock_spinlock(void *__map) ...@@ -312,6 +313,7 @@ static void regmap_lock_spinlock(void *__map)
} }
static void regmap_unlock_spinlock(void *__map) static void regmap_unlock_spinlock(void *__map)
__releases(&map->spinlock)
{ {
struct regmap *map = __map; struct regmap *map = __map;
spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags); spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags);
...@@ -687,6 +689,10 @@ struct regmap *regmap_init(struct device *dev, ...@@ -687,6 +689,10 @@ struct regmap *regmap_init(struct device *dev,
unsigned win_max = win_min + unsigned win_max = win_min +
config->ranges[j].window_len - 1; config->ranges[j].window_len - 1;
/* Allow data window inside its own virtual range */
if (j == i)
continue;
if (range_cfg->range_min <= sel_reg && if (range_cfg->range_min <= sel_reg &&
sel_reg <= range_cfg->range_max) { sel_reg <= range_cfg->range_max) {
dev_err(map->dev, dev_err(map->dev,
...@@ -1261,6 +1267,9 @@ int _regmap_write(struct regmap *map, unsigned int reg, ...@@ -1261,6 +1267,9 @@ int _regmap_write(struct regmap *map, unsigned int reg,
int ret; int ret;
void *context = _regmap_map_get_context(map); void *context = _regmap_map_get_context(map);
if (!regmap_writeable(map, reg))
return -EIO;
if (!map->cache_bypass && !map->defer_caching) { if (!map->cache_bypass && !map->defer_caching) {
ret = regcache_write(map, reg, val); ret = regcache_write(map, reg, val);
if (ret != 0) if (ret != 0)
...@@ -1888,13 +1897,10 @@ EXPORT_SYMBOL_GPL(regmap_async_complete); ...@@ -1888,13 +1897,10 @@ EXPORT_SYMBOL_GPL(regmap_async_complete);
int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
int num_regs) int num_regs)
{ {
struct reg_default *p;
int i, ret; int i, ret;
bool bypass; bool bypass;
/* If needed the implementation can be extended to support this */
if (map->patch)
return -EBUSY;
map->lock(map->lock_arg); map->lock(map->lock_arg);
bypass = map->cache_bypass; bypass = map->cache_bypass;
...@@ -1911,11 +1917,13 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, ...@@ -1911,11 +1917,13 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
} }
} }
map->patch = kcalloc(num_regs, sizeof(struct reg_default), GFP_KERNEL); p = krealloc(map->patch,
if (map->patch != NULL) { sizeof(struct reg_default) * (map->patch_regs + num_regs),
memcpy(map->patch, regs, GFP_KERNEL);
num_regs * sizeof(struct reg_default)); if (p) {
map->patch_regs = num_regs; memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs));
map->patch = p;
map->patch_regs += num_regs;
} else { } else {
ret = -ENOMEM; ret = -ENOMEM;
} }
......
...@@ -472,6 +472,9 @@ struct regmap_irq { ...@@ -472,6 +472,9 @@ struct regmap_irq {
* @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.
* @wake_base: Base address for wake enables. If zero unsupported. * @wake_base: Base address for wake enables. If zero unsupported.
* @irq_reg_stride: Stride to use for chips where registers are not contiguous. * @irq_reg_stride: Stride to use for chips where registers are not contiguous.
* @init_ack_masked: Ack all masked interrupts once during initalization.
* @mask_invert: Inverted mask register: cleared bits are masked out.
* @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.
* *
* @num_regs: Number of registers in each control bank. * @num_regs: Number of registers in each control bank.
...@@ -487,9 +490,10 @@ struct regmap_irq_chip { ...@@ -487,9 +490,10 @@ struct regmap_irq_chip {
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;
unsigned int mask_invert; bool init_ack_masked:1;
unsigned int wake_invert; bool mask_invert:1;
bool runtime_pm; bool wake_invert:1;
bool runtime_pm:1;
int num_regs; int num_regs;
......
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