Commit ae82a828 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull regmap updates from Mark Brown:
 "A surprisingly large series of updates for regmap this time, mostly
  due to all the work Stephen Warren has done to add support for MMIO
  buses.  This wasn't really the target for the framework but it turns
  out that there's a reasonable number of cases where it's very helpful
  to use the register cache support to allow the register map to remain
  available while the device is suspended.

  - A MMIO bus implementation, contributed by Stephen Warren.  Currently
    this is limited to 32 bit systems and native endian registers.
  - Support for naming register maps, mainly intended for MMIO devices
    with multiple register banks.  This was also contributed by Stephen
    Warren.
  - Support for register striding, again contributed by Stephen Warren
    and mainly intended for use with MMIO as typically the registers
    will be a fixed size but byte addressed.
  - irqdomain support for the generic regmap irq_chip, including support
    for dynamically allocate interrupt numbers.
  - A function dev_get_regmap() which allows frameworks using regmap to
    obtain the regmap for a device from the struct device, making life a
    little simpler for them.
  - Updates to regmap-irq to support more chips (contributed by Graeme
    Gregory) and to use irqdomains.
  - Support for devices with 24 bit register addresses.

  The striding support collided with all the topic branches so the
  branches look a bit messy and eventually I just gave up.  There's also
  the TI Palmas driver and a couple of other isolated MFD patches that
  all depend on new regmap features so are being merged here."

* tag 'regmap-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: (24 commits)
  mfd: palmas PMIC device support Kconfig
  mfd: palmas PMIC device support
  regmap: Fix typo in IRQ register striding
  mfd: wm8994: Update to fully use irq_domain
  regmap: add support for non contiguous status to regmap-irq
  regmap: Convert regmap_irq to use irq_domain
  regmap: Pass back the allocated regmap IRQ controller data
  mfd: da9052: Fix genirq abuse
  regmap: Implement dev_get_regmap()
  regmap: Devices using format_write don't support bulk operations
  regmap: Converts group operation into single read write operations
  regmap: Cache single values read from the chip
  regmap: fix compile errors in regmap-irq.c due to stride changes
  regmap: implement register striding
  regmap: fix compilation when !CONFIG_DEBUG_FS
  regmap: allow regmap instances to be named
  regmap: validate regmap_raw_read/write val_len
  regmap: mmio: remove some error checks now in the core
  regmap: mmio: convert some error returns to BUG()
  regmap: add MMIO bus support
  ...
parents 3bb07f1b c948ef3a
......@@ -14,5 +14,8 @@ config REGMAP_I2C
config REGMAP_SPI
tristate
config REGMAP_MMIO
tristate
config REGMAP_IRQ
bool
......@@ -3,4 +3,5 @@ obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o
obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o
......@@ -26,21 +26,30 @@ struct regmap_format {
size_t val_bytes;
void (*format_write)(struct regmap *map,
unsigned int reg, unsigned int val);
void (*format_reg)(void *buf, unsigned int reg);
void (*format_val)(void *buf, unsigned int val);
void (*format_reg)(void *buf, unsigned int reg, unsigned int shift);
void (*format_val)(void *buf, unsigned int val, unsigned int shift);
unsigned int (*parse_val)(void *buf);
};
typedef void (*regmap_lock)(struct regmap *map);
typedef void (*regmap_unlock)(struct regmap *map);
struct regmap {
struct mutex lock;
struct mutex mutex;
spinlock_t spinlock;
regmap_lock lock;
regmap_unlock unlock;
struct device *dev; /* Device we do I/O on */
void *work_buf; /* Scratch buffer used to format I/O */
struct regmap_format format; /* Buffer format */
const struct regmap_bus *bus;
void *bus_context;
const char *name;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
const char *debugfs_name;
#endif
unsigned int max_register;
......@@ -52,6 +61,10 @@ struct regmap {
u8 read_flag_mask;
u8 write_flag_mask;
/* number of bits to (left) shift the reg value when formatting*/
int reg_shift;
int reg_stride;
/* regcache specific members */
const struct regcache_ops *cache_ops;
enum regcache_type cache_type;
......@@ -79,6 +92,9 @@ struct regmap {
struct reg_default *patch;
int patch_regs;
/* if set, converts bulk rw to single rw */
bool use_single_rw;
};
struct regcache_ops {
......@@ -101,11 +117,11 @@ int _regmap_write(struct regmap *map, unsigned int reg,
#ifdef CONFIG_DEBUG_FS
extern void regmap_debugfs_initcall(void);
extern void regmap_debugfs_init(struct regmap *map);
extern void regmap_debugfs_init(struct regmap *map, const char *name);
extern void regmap_debugfs_exit(struct regmap *map);
#else
static inline void regmap_debugfs_initcall(void) { }
static inline void regmap_debugfs_init(struct regmap *map) { }
static inline void regmap_debugfs_init(struct regmap *map, const char *name) { }
static inline void regmap_debugfs_exit(struct regmap *map) { }
#endif
......
......@@ -108,7 +108,7 @@ static int regcache_lzo_decompress_cache_block(struct regmap *map,
static inline int regcache_lzo_get_blkindex(struct regmap *map,
unsigned int reg)
{
return (reg * map->cache_word_size) /
return ((reg / map->reg_stride) * map->cache_word_size) /
DIV_ROUND_UP(map->cache_size_raw,
regcache_lzo_block_count(map));
}
......@@ -116,9 +116,10 @@ static inline int regcache_lzo_get_blkindex(struct regmap *map,
static inline int regcache_lzo_get_blkpos(struct regmap *map,
unsigned int reg)
{
return reg % (DIV_ROUND_UP(map->cache_size_raw,
regcache_lzo_block_count(map)) /
map->cache_word_size);
return (reg / map->reg_stride) %
(DIV_ROUND_UP(map->cache_size_raw,
regcache_lzo_block_count(map)) /
map->cache_word_size);
}
static inline int regcache_lzo_get_blksize(struct regmap *map)
......@@ -322,7 +323,7 @@ static int regcache_lzo_write(struct regmap *map,
}
/* set the bit so we know we have to sync this register */
set_bit(reg, lzo_block->sync_bmp);
set_bit(reg / map->reg_stride, lzo_block->sync_bmp);
kfree(tmp_dst);
kfree(lzo_block->src);
return 0;
......
......@@ -39,11 +39,12 @@ struct regcache_rbtree_ctx {
};
static inline void regcache_rbtree_get_base_top_reg(
struct regmap *map,
struct regcache_rbtree_node *rbnode,
unsigned int *base, unsigned int *top)
{
*base = rbnode->base_reg;
*top = rbnode->base_reg + rbnode->blklen - 1;
*top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride);
}
static unsigned int regcache_rbtree_get_register(
......@@ -70,7 +71,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
rbnode = rbtree_ctx->cached_rbnode;
if (rbnode) {
regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
&top_reg);
if (reg >= base_reg && reg <= top_reg)
return rbnode;
}
......@@ -78,7 +80,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
node = rbtree_ctx->root.rb_node;
while (node) {
rbnode = container_of(node, struct regcache_rbtree_node, node);
regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
&top_reg);
if (reg >= base_reg && reg <= top_reg) {
rbtree_ctx->cached_rbnode = rbnode;
return rbnode;
......@@ -92,7 +95,7 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
return NULL;
}
static int regcache_rbtree_insert(struct rb_root *root,
static int regcache_rbtree_insert(struct regmap *map, struct rb_root *root,
struct regcache_rbtree_node *rbnode)
{
struct rb_node **new, *parent;
......@@ -106,7 +109,7 @@ static int regcache_rbtree_insert(struct rb_root *root,
rbnode_tmp = container_of(*new, struct regcache_rbtree_node,
node);
/* base and top registers of the current rbnode */
regcache_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp,
regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp,
&top_reg_tmp);
/* base register of the rbnode to be added */
base_reg = rbnode->base_reg;
......@@ -138,19 +141,20 @@ static int rbtree_show(struct seq_file *s, void *ignored)
unsigned int base, top;
int nodes = 0;
int registers = 0;
int average;
int this_registers, average;
mutex_lock(&map->lock);
map->lock(map);
for (node = rb_first(&rbtree_ctx->root); node != NULL;
node = rb_next(node)) {
n = container_of(node, struct regcache_rbtree_node, node);
regcache_rbtree_get_base_top_reg(n, &base, &top);
seq_printf(s, "%x-%x (%d)\n", base, top, top - base + 1);
regcache_rbtree_get_base_top_reg(map, n, &base, &top);
this_registers = ((top - base) / map->reg_stride) + 1;
seq_printf(s, "%x-%x (%d)\n", base, top, this_registers);
nodes++;
registers += top - base + 1;
registers += this_registers;
}
if (nodes)
......@@ -161,7 +165,7 @@ static int rbtree_show(struct seq_file *s, void *ignored)
seq_printf(s, "%d nodes, %d registers, average %d registers\n",
nodes, registers, average);
mutex_unlock(&map->lock);
map->unlock(map);
return 0;
}
......@@ -255,7 +259,7 @@ static int regcache_rbtree_read(struct regmap *map,
rbnode = regcache_rbtree_lookup(map, reg);
if (rbnode) {
reg_tmp = reg - rbnode->base_reg;
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
*value = regcache_rbtree_get_register(rbnode, reg_tmp,
map->cache_word_size);
} else {
......@@ -310,7 +314,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
*/
rbnode = regcache_rbtree_lookup(map, reg);
if (rbnode) {
reg_tmp = reg - rbnode->base_reg;
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
val = regcache_rbtree_get_register(rbnode, reg_tmp,
map->cache_word_size);
if (val == value)
......@@ -321,13 +325,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
/* look for an adjacent register to the one we are about to add */
for (node = rb_first(&rbtree_ctx->root); node;
node = rb_next(node)) {
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node);
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
node);
for (i = 0; i < rbnode_tmp->blklen; i++) {
reg_tmp = rbnode_tmp->base_reg + i;
if (abs(reg_tmp - reg) != 1)
reg_tmp = rbnode_tmp->base_reg +
(i * map->reg_stride);
if (abs(reg_tmp - reg) != map->reg_stride)
continue;
/* decide where in the block to place our register */
if (reg_tmp + 1 == reg)
if (reg_tmp + map->reg_stride == reg)
pos = i + 1;
else
pos = i;
......@@ -357,7 +363,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
return -ENOMEM;
}
regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size);
regcache_rbtree_insert(&rbtree_ctx->root, rbnode);
regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode);
rbtree_ctx->cached_rbnode = rbnode;
}
......@@ -397,7 +403,7 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
end = rbnode->blklen;
for (i = base; i < end; i++) {
regtmp = rbnode->base_reg + i;
regtmp = rbnode->base_reg + (i * map->reg_stride);
val = regcache_rbtree_get_register(rbnode, i,
map->cache_word_size);
......
......@@ -59,7 +59,7 @@ static int regcache_hw_init(struct regmap *map)
for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) {
val = regcache_get_val(map->reg_defaults_raw,
i, map->cache_word_size);
if (regmap_volatile(map, i))
if (regmap_volatile(map, i * map->reg_stride))
continue;
count++;
}
......@@ -76,9 +76,9 @@ static int regcache_hw_init(struct regmap *map)
for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
val = regcache_get_val(map->reg_defaults_raw,
i, map->cache_word_size);
if (regmap_volatile(map, i))
if (regmap_volatile(map, i * map->reg_stride))
continue;
map->reg_defaults[j].reg = i;
map->reg_defaults[j].reg = i * map->reg_stride;
map->reg_defaults[j].def = val;
j++;
}
......@@ -98,6 +98,10 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
int i;
void *tmp_buf;
for (i = 0; i < config->num_reg_defaults; i++)
if (config->reg_defaults[i].reg % map->reg_stride)
return -EINVAL;
if (map->cache_type == REGCACHE_NONE) {
map->cache_bypass = true;
return 0;
......@@ -264,7 +268,7 @@ int regcache_sync(struct regmap *map)
BUG_ON(!map->cache_ops || !map->cache_ops->sync);
mutex_lock(&map->lock);
map->lock(map);
/* Remember the initial bypass state */
bypass = map->cache_bypass;
dev_dbg(map->dev, "Syncing %s cache\n",
......@@ -278,6 +282,10 @@ 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",
......@@ -296,7 +304,7 @@ int regcache_sync(struct regmap *map)
trace_regcache_sync(map->dev, name, "stop");
/* Restore the bypass state */
map->cache_bypass = bypass;
mutex_unlock(&map->lock);
map->unlock(map);
return ret;
}
......@@ -323,7 +331,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
BUG_ON(!map->cache_ops || !map->cache_ops->sync);
mutex_lock(&map->lock);
map->lock(map);
/* Remember the initial bypass state */
bypass = map->cache_bypass;
......@@ -342,7 +350,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
trace_regcache_sync(map->dev, name, "stop region");
/* Restore the bypass state */
map->cache_bypass = bypass;
mutex_unlock(&map->lock);
map->unlock(map);
return ret;
}
......@@ -362,11 +370,11 @@ EXPORT_SYMBOL_GPL(regcache_sync_region);
*/
void regcache_cache_only(struct regmap *map, bool enable)
{
mutex_lock(&map->lock);
map->lock(map);
WARN_ON(map->cache_bypass && enable);
map->cache_only = enable;
trace_regmap_cache_only(map->dev, enable);
mutex_unlock(&map->lock);
map->unlock(map);
}
EXPORT_SYMBOL_GPL(regcache_cache_only);
......@@ -381,9 +389,9 @@ EXPORT_SYMBOL_GPL(regcache_cache_only);
*/
void regcache_mark_dirty(struct regmap *map)
{
mutex_lock(&map->lock);
map->lock(map);
map->cache_dirty = true;
mutex_unlock(&map->lock);
map->unlock(map);
}
EXPORT_SYMBOL_GPL(regcache_mark_dirty);
......@@ -400,11 +408,11 @@ EXPORT_SYMBOL_GPL(regcache_mark_dirty);
*/
void regcache_cache_bypass(struct regmap *map, bool enable)
{
mutex_lock(&map->lock);
map->lock(map);
WARN_ON(map->cache_only && enable);
map->cache_bypass = enable;
trace_regmap_cache_bypass(map->dev, enable);
mutex_unlock(&map->lock);
map->unlock(map);
}
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
......
......@@ -80,7 +80,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
val_len = 2 * map->format.val_bytes;
tot_len = reg_len + val_len + 3; /* : \n */
for (i = 0; i < map->max_register + 1; i++) {
for (i = 0; i <= map->max_register; i += map->reg_stride) {
if (!regmap_readable(map, i))
continue;
......@@ -197,7 +197,7 @@ static ssize_t regmap_access_read_file(struct file *file,
reg_len = regmap_calc_reg_len(map->max_register, buf, count);
tot_len = reg_len + 10; /* ': R W V P\n' */
for (i = 0; i < map->max_register + 1; i++) {
for (i = 0; i <= map->max_register; i += map->reg_stride) {
/* Ignore registers which are neither readable nor writable */
if (!regmap_readable(map, i) && !regmap_writeable(map, i))
continue;
......@@ -242,10 +242,17 @@ static const struct file_operations regmap_access_fops = {
.llseek = default_llseek,
};
void regmap_debugfs_init(struct regmap *map)
void regmap_debugfs_init(struct regmap *map, const char *name)
{
map->debugfs = debugfs_create_dir(dev_name(map->dev),
regmap_debugfs_root);
if (name) {
map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
dev_name(map->dev), name);
name = map->debugfs_name;
} else {
name = dev_name(map->dev);
}
map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
if (!map->debugfs) {
dev_warn(map->dev, "Failed to create debugfs directory\n");
return;
......@@ -274,6 +281,7 @@ void regmap_debugfs_init(struct regmap *map)
void regmap_debugfs_exit(struct regmap *map)
{
debugfs_remove_recursive(map->debugfs);
kfree(map->debugfs_name);
}
void regmap_debugfs_initcall(void)
......
......@@ -15,8 +15,9 @@
#include <linux/module.h>
#include <linux/init.h>
static int regmap_i2c_write(struct device *dev, const void *data, size_t count)
static int regmap_i2c_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
int ret;
......@@ -29,10 +30,11 @@ static int regmap_i2c_write(struct device *dev, const void *data, size_t count)
return -EIO;
}
static int regmap_i2c_gather_write(struct device *dev,
static int regmap_i2c_gather_write(void *context,
const void *reg, size_t reg_size,
const void *val, size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct i2c_msg xfer[2];
int ret;
......@@ -62,10 +64,11 @@ static int regmap_i2c_gather_write(struct device *dev,
return -EIO;
}
static int regmap_i2c_read(struct device *dev,
static int regmap_i2c_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct i2c_msg xfer[2];
int ret;
......@@ -107,7 +110,7 @@ static struct regmap_bus regmap_i2c = {
struct regmap *regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config)
{
return regmap_init(&i2c->dev, &regmap_i2c, config);
return regmap_init(&i2c->dev, &regmap_i2c, &i2c->dev, config);
}
EXPORT_SYMBOL_GPL(regmap_init_i2c);
......@@ -124,7 +127,7 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c);
struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config)
{
return devm_regmap_init(&i2c->dev, &regmap_i2c, config);
return devm_regmap_init(&i2c->dev, &regmap_i2c, &i2c->dev, config);
}
EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
......
......@@ -15,6 +15,7 @@
#include <linux/regmap.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/slab.h>
#include "internal.h"
......@@ -26,18 +27,20 @@ struct regmap_irq_chip_data {
struct regmap_irq_chip *chip;
int irq_base;
struct irq_domain *domain;
void *status_reg_buf;
unsigned int *status_buf;
unsigned int *mask_buf;
unsigned int *mask_buf_def;
unsigned int irq_reg_stride;
};
static inline const
struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,
int irq)
{
return &data->chip->irqs[irq - data->irq_base];
return &data->chip->irqs[irq];
}
static void regmap_irq_lock(struct irq_data *data)
......@@ -50,6 +53,7 @@ static void regmap_irq_lock(struct irq_data *data)
static void regmap_irq_sync_unlock(struct irq_data *data)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
struct regmap *map = d->map;
int i, ret;
/*
......@@ -58,11 +62,13 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* suppress pointless writes.
*/
for (i = 0; i < d->chip->num_regs; i++) {
ret = regmap_update_bits(d->map, d->chip->mask_base + i,
ret = regmap_update_bits(d->map, d->chip->mask_base +
(i * map->reg_stride *
d->irq_reg_stride),
d->mask_buf_def[i], d->mask_buf[i]);
if (ret != 0)
dev_err(d->map->dev, "Failed to sync masks in %x\n",
d->chip->mask_base + i);
d->chip->mask_base + (i * map->reg_stride));
}
mutex_unlock(&d->lock);
......@@ -71,17 +77,19 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
static void regmap_irq_enable(struct irq_data *data)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq);
struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
d->mask_buf[irq_data->reg_offset] &= ~irq_data->mask;
d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask;
}
static void regmap_irq_disable(struct irq_data *data)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq);
struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
d->mask_buf[irq_data->reg_offset] |= irq_data->mask;
d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
}
static struct irq_chip regmap_irq_chip = {
......@@ -98,18 +106,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
int ret, i;
u8 *buf8 = data->status_reg_buf;
u16 *buf16 = data->status_reg_buf;
u32 *buf32 = data->status_reg_buf;
bool handled = false;
ret = regmap_bulk_read(map, chip->status_base, data->status_reg_buf,
chip->num_regs);
if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n", ret);
return IRQ_NONE;
}
/*
* Ignore masked IRQs and ack if we need to; we ack early so
* there is no race between handling and acknowleding the
......@@ -118,36 +116,34 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
* doing a write per register.
*/
for (i = 0; i < data->chip->num_regs; i++) {
switch (map->format.val_bytes) {
case 1:
data->status_buf[i] = buf8[i];
break;
case 2:
data->status_buf[i] = buf16[i];
break;
case 4:
data->status_buf[i] = buf32[i];
break;
default:
BUG();
ret = regmap_read(map, chip->status_base + (i * map->reg_stride
* data->irq_reg_stride),
&data->status_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n",
ret);
return IRQ_NONE;
}
data->status_buf[i] &= ~data->mask_buf[i];
if (data->status_buf[i] && chip->ack_base) {
ret = regmap_write(map, chip->ack_base + i,
ret = regmap_write(map, chip->ack_base +
(i * map->reg_stride *
data->irq_reg_stride),
data->status_buf[i]);
if (ret != 0)
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
chip->ack_base + i, ret);
chip->ack_base + (i * map->reg_stride),
ret);
}
}
for (i = 0; i < chip->num_irqs; i++) {
if (data->status_buf[chip->irqs[i].reg_offset] &
chip->irqs[i].mask) {
handle_nested_irq(data->irq_base + i);
if (data->status_buf[chip->irqs[i].reg_offset /
map->reg_stride] & chip->irqs[i].mask) {
handle_nested_irq(irq_find_mapping(data->domain, i));
handled = true;
}
}
......@@ -158,6 +154,31 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
return IRQ_NONE;
}
static int regmap_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct regmap_irq_chip_data *data = h->host_data;
irq_set_chip_data(virq, data);
irq_set_chip_and_handler(virq, &regmap_irq_chip, handle_edge_irq);
irq_set_nested_thread(virq, 1);
/* ARM needs us to explicitly flag the IRQ as valid
* and will set them noprobe when we do so. */
#ifdef CONFIG_ARM
set_irq_flags(virq, IRQF_VALID);
#else
irq_set_noprobe(virq);
#endif
return 0;
}
static struct irq_domain_ops regmap_domain_ops = {
.map = regmap_irq_map,
.xlate = irq_domain_xlate_twocell,
};
/**
* regmap_add_irq_chip(): Use standard regmap IRQ controller handling
*
......@@ -178,30 +199,37 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
struct regmap_irq_chip_data **data)
{
struct regmap_irq_chip_data *d;
int cur_irq, i;
int i;
int ret = -ENOMEM;
irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
if (irq_base < 0) {
dev_warn(map->dev, "Failed to allocate IRQs: %d\n",
irq_base);
return irq_base;
for (i = 0; i < chip->num_irqs; i++) {
if (chip->irqs[i].reg_offset % map->reg_stride)
return -EINVAL;
if (chip->irqs[i].reg_offset / map->reg_stride >=
chip->num_regs)
return -EINVAL;
}
if (irq_base) {
irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
if (irq_base < 0) {
dev_warn(map->dev, "Failed to allocate IRQs: %d\n",
irq_base);
return irq_base;
}
}
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d)
return -ENOMEM;
*data = d;
d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
GFP_KERNEL);
if (!d->status_buf)
goto err_alloc;
d->status_reg_buf = kzalloc(map->format.val_bytes * chip->num_regs,
GFP_KERNEL);
if (!d->status_reg_buf)
goto err_alloc;
d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
GFP_KERNEL);
if (!d->mask_buf)
......@@ -215,54 +243,59 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
d->map = map;
d->chip = chip;
d->irq_base = irq_base;
if (chip->irq_reg_stride)
d->irq_reg_stride = chip->irq_reg_stride;
else
d->irq_reg_stride = 1;
mutex_init(&d->lock);
for (i = 0; i < chip->num_irqs; i++)
d->mask_buf_def[chip->irqs[i].reg_offset]
d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride]
|= chip->irqs[i].mask;