regmap-irq cleanups and refactoring

Merge series from Aidan MacDonald <aidanmacdonald.0x0@gmail.com>:

This series is an attempt at cleaning up the regmap-irq API in order
to simplify things and consolidate existing features, while at the
same time generalizing it to support a wider range of hardware.

There is a new system for IRQ type configuration, some tweaks to
unmask registers so they're more intuitive and useful, and a new
callback for calculating register addresses. There's also a few
minor code cleanups in here.

In v2 I've taken the approach of adding new features and deprecating
existing ones rather than removing them aggressively. Warnings will
be issued for any drivers that use deprecated features, but they'll
otherwise continue to function normally.

One important caveat: not all of these changes are tested beyond
compile testing, since I don't have hardware to exercise all of
the features.
This commit is contained in:
Mark Brown 2022-06-30 18:26:37 +01:00
commit e129e41381
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 369 additions and 171 deletions

View File

@ -30,6 +30,9 @@ struct regmap_irq_chip_data {
int irq;
int wake_count;
unsigned int mask_base;
unsigned int unmask_base;
void *status_reg_buf;
unsigned int *main_status_buf;
unsigned int *status_buf;
@ -39,34 +42,16 @@ struct regmap_irq_chip_data {
unsigned int *type_buf;
unsigned int *type_buf_def;
unsigned int **virt_buf;
unsigned int **config_buf;
unsigned int irq_reg_stride;
unsigned int type_reg_stride;
bool clear_status:1;
unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data,
unsigned int base, int index);
unsigned int clear_status:1;
};
static int sub_irq_reg(struct regmap_irq_chip_data *data,
unsigned int base_reg, int i)
{
const struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
struct regmap_irq_sub_irq_map *subreg;
unsigned int offset;
int reg = 0;
if (!chip->sub_reg_offsets || !chip->not_fixed_stride) {
/* Assume linear mapping */
reg = base_reg + (i * map->reg_stride * data->irq_reg_stride);
} else {
subreg = &chip->sub_reg_offsets[i];
offset = subreg->offset[0];
reg = base_reg + offset;
}
return reg;
}
static inline const
struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,
int irq)
@ -74,6 +59,20 @@ struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,
return &data->chip->irqs[irq];
}
static bool regmap_irq_can_bulk_read_status(struct regmap_irq_chip_data *data)
{
struct regmap *map = data->map;
/*
* While possible that a user-defined ->get_irq_reg() callback might
* be linear enough to support bulk reads, most of the time it won't.
* Therefore only allow them if the default callback is being used.
*/
return data->irq_reg_stride == 1 && map->reg_stride == 1 &&
data->get_irq_reg == regmap_irq_get_irq_reg_linear &&
!map->use_single_read;
}
static void regmap_irq_lock(struct irq_data *data)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
@ -81,23 +80,12 @@ static void regmap_irq_lock(struct irq_data *data)
mutex_lock(&d->lock);
}
static int regmap_irq_update_bits(struct regmap_irq_chip_data *d,
unsigned int reg, unsigned int mask,
unsigned int val)
{
if (d->chip->mask_writeonly)
return regmap_write_bits(d->map, reg, mask, val);
else
return regmap_update_bits(d->map, reg, mask, val);
}
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, j, ret;
u32 reg;
u32 unmask_offset;
u32 val;
if (d->chip->runtime_pm) {
@ -109,7 +97,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
if (d->clear_status) {
for (i = 0; i < d->chip->num_regs; i++) {
reg = sub_irq_reg(d, d->chip->status_base, i);
reg = d->get_irq_reg(d, d->chip->status_base, i);
ret = regmap_read(map, reg, &val);
if (ret)
@ -126,44 +114,32 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* suppress pointless writes.
*/
for (i = 0; i < d->chip->num_regs; i++) {
if (!d->chip->mask_base)
continue;
reg = sub_irq_reg(d, d->chip->mask_base, i);
if (d->chip->mask_invert) {
ret = regmap_irq_update_bits(d, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
} else if (d->chip->unmask_base) {
/* set mask with mask_base register */
ret = regmap_irq_update_bits(d, 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_irq_update_bits(d,
reg + unmask_offset,
d->mask_buf_def[i],
d->mask_buf[i]);
} else {
ret = regmap_irq_update_bits(d, reg,
if (d->mask_base) {
reg = d->get_irq_reg(d, d->mask_base, i);
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], d->mask_buf[i]);
}
if (ret != 0)
if (ret)
dev_err(d->map->dev, "Failed to sync masks in %x\n",
reg);
}
reg = sub_irq_reg(d, d->chip->wake_base, i);
if (d->unmask_base) {
reg = d->get_irq_reg(d, d->unmask_base, i);
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret)
dev_err(d->map->dev, "Failed to sync masks in %x\n",
reg);
}
reg = d->get_irq_reg(d, d->chip->wake_base, i);
if (d->wake_buf) {
if (d->chip->wake_invert)
ret = regmap_irq_update_bits(d, reg,
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i],
~d->wake_buf[i]);
else
ret = regmap_irq_update_bits(d, reg,
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i],
d->wake_buf[i]);
if (ret != 0)
@ -180,7 +156,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* it'll be ignored in irq handler, then may introduce irq storm
*/
if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) {
reg = sub_irq_reg(d, d->chip->ack_base, i);
reg = d->get_irq_reg(d, d->chip->ack_base, i);
/* some chips ack by write 0 */
if (d->chip->ack_invert)
@ -204,12 +180,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
for (i = 0; i < d->chip->num_type_reg; i++) {
if (!d->type_buf_def[i])
continue;
reg = sub_irq_reg(d, d->chip->type_base, i);
reg = d->get_irq_reg(d, d->chip->type_base, i);
if (d->chip->type_invert)
ret = regmap_irq_update_bits(d, reg,
ret = regmap_update_bits(d->map, reg,
d->type_buf_def[i], ~d->type_buf[i]);
else
ret = regmap_irq_update_bits(d, reg,
ret = regmap_update_bits(d->map, reg,
d->type_buf_def[i], d->type_buf[i]);
if (ret != 0)
dev_err(d->map->dev, "Failed to sync type in %x\n",
@ -220,7 +196,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
if (d->chip->num_virt_regs) {
for (i = 0; i < d->chip->num_virt_regs; i++) {
for (j = 0; j < d->chip->num_regs; j++) {
reg = sub_irq_reg(d, d->chip->virt_reg_base[i],
reg = d->get_irq_reg(d, d->chip->virt_reg_base[i],
j);
ret = regmap_write(map, reg, d->virt_buf[i][j]);
if (ret != 0)
@ -231,6 +207,17 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
}
}
for (i = 0; i < d->chip->num_config_bases; i++) {
for (j = 0; j < d->chip->num_config_regs; j++) {
reg = d->get_irq_reg(d, d->chip->config_base[i], j);
ret = regmap_write(map, reg, d->config_buf[i][j]);
if (ret)
dev_err(d->map->dev,
"Failed to write config %x: %d\n",
reg, ret);
}
}
if (d->chip->runtime_pm)
pm_runtime_put(map->dev);
@ -253,22 +240,19 @@ static void regmap_irq_enable(struct irq_data *data)
struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
unsigned int reg = irq_data->reg_offset / map->reg_stride;
unsigned int mask, type;
type = irq_data->type.type_falling_val | irq_data->type.type_rising_val;
unsigned int mask;
/*
* The type_in_mask flag means that the underlying hardware uses
* separate mask bits for rising and falling edge interrupts, but
* we want to make them into a single virtual interrupt with
* configurable edge.
* separate mask bits for each interrupt trigger type, but we want
* to have a single logical interrupt with a configurable type.
*
* If the interrupt we're enabling defines the falling or rising
* masks then instead of using the regular mask bits for this
* interrupt, use the value previously written to the type buffer
* at the corresponding offset in regmap_irq_set_type().
* If the interrupt we're enabling defines any supported types
* then instead of using the regular mask bits for this interrupt,
* use the value previously written to the type buffer at the
* corresponding offset in regmap_irq_set_type().
*/
if (d->chip->type_in_mask && type)
if (d->chip->type_in_mask && irq_data->type.types_supported)
mask = d->type_buf[reg] & irq_data->mask;
else
mask = irq_data->mask;
@ -293,7 +277,7 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
int reg;
int reg, ret;
const struct regmap_irq_type *t = &irq_data->type;
if ((t->types_supported & type) != type)
@ -333,9 +317,19 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
return -EINVAL;
}
if (d->chip->set_type_virt)
return d->chip->set_type_virt(d->virt_buf, type, data->hwirq,
if (d->chip->set_type_virt) {
ret = d->chip->set_type_virt(d->virt_buf, type, data->hwirq,
reg);
if (ret)
return ret;
}
if (d->chip->set_type_config) {
ret = d->chip->set_type_config(d->config_buf, type,
irq_data, reg);
if (ret)
return ret;
}
return 0;
}
@ -376,14 +370,17 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
const struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
struct regmap_irq_sub_irq_map *subreg;
unsigned int reg;
int i, ret = 0;
if (!chip->sub_reg_offsets) {
/* Assume linear mapping */
ret = regmap_read(map, chip->status_base +
(b * map->reg_stride * data->irq_reg_stride),
&data->status_buf[b]);
reg = data->get_irq_reg(data, chip->status_base, b);
ret = regmap_read(map, reg, &data->status_buf[b]);
} else {
/*
* Note we can't use ->get_irq_reg() here because the offsets
* in 'subreg' are *not* interchangeable with indices.
*/
subreg = &chip->sub_reg_offsets[b];
for (i = 0; i < subreg->num_regs; i++) {
unsigned int offset = subreg->offset[i];
@ -449,10 +446,18 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
* sake of simplicity. and add bulk reads only if needed
*/
for (i = 0; i < chip->num_main_regs; i++) {
ret = regmap_read(map, chip->main_status +
(i * map->reg_stride
* data->irq_reg_stride),
&data->main_status_buf[i]);
/*
* For not_fixed_stride, don't use ->get_irq_reg().
* It would produce an incorrect result.
*/
if (data->chip->not_fixed_stride)
reg = chip->main_status +
i * map->reg_stride * data->irq_reg_stride;
else
reg = data->get_irq_reg(data,
chip->main_status, i);
ret = regmap_read(map, reg, &data->main_status_buf[i]);
if (ret) {
dev_err(map->dev,
"Failed to read IRQ status %d\n",
@ -481,8 +486,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
}
}
} else if (!map->use_single_read && map->reg_stride == 1 &&
data->irq_reg_stride == 1) {
} else if (regmap_irq_can_bulk_read_status(data)) {
u8 *buf8 = data->status_reg_buf;
u16 *buf16 = data->status_reg_buf;
@ -518,7 +522,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
} else {
for (i = 0; i < data->chip->num_regs; i++) {
unsigned int reg = sub_irq_reg(data,
unsigned int reg = data->get_irq_reg(data,
data->chip->status_base, i);
ret = regmap_read(map, reg, &data->status_buf[i]);
@ -546,7 +550,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
data->status_buf[i] &= ~data->mask_buf[i];
if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) {
reg = sub_irq_reg(data, data->chip->ack_base, i);
reg = data->get_irq_reg(data, data->chip->ack_base, i);
if (chip->ack_invert)
ret = regmap_write(map, reg,
@ -606,6 +610,91 @@ static const struct irq_domain_ops regmap_domain_ops = {
.xlate = irq_domain_xlate_onetwocell,
};
/**
* regmap_irq_get_irq_reg_linear() - Linear IRQ register mapping callback.
* @data: Data for the &struct regmap_irq_chip
* @base: Base register
* @index: Register index
*
* Returns the register address corresponding to the given @base and @index
* by the formula ``base + index * regmap_stride * irq_reg_stride``.
*/
unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data,
unsigned int base, int index)
{
const struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
/*
* FIXME: This is for backward compatibility and should be removed
* when not_fixed_stride is dropped (it's only used by qcom-pm8008).
*/
if (chip->not_fixed_stride && chip->sub_reg_offsets) {
struct regmap_irq_sub_irq_map *subreg;
subreg = &chip->sub_reg_offsets[0];
return base + subreg->offset[0];
}
return base + index * map->reg_stride * chip->irq_reg_stride;
}
EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear);
/**
* regmap_irq_set_type_config_simple() - Simple IRQ type configuration callback.
* @buf: Buffer containing configuration register values, this is a 2D array of
* `num_config_bases` rows, each of `num_config_regs` elements.
* @type: The requested IRQ type.
* @irq_data: The IRQ being configured.
* @idx: Index of the irq's config registers within each array `buf[i]`
*
* This is a &struct regmap_irq_chip->set_type_config callback suitable for
* chips with one config register. Register values are updated according to
* the &struct regmap_irq_type data associated with an IRQ.
*/
int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx)
{
const struct regmap_irq_type *t = &irq_data->type;
if (t->type_reg_mask)
buf[0][idx] &= ~t->type_reg_mask;
else
buf[0][idx] &= ~(t->type_falling_val |
t->type_rising_val |
t->type_level_low_val |
t->type_level_high_val);
switch (type) {
case IRQ_TYPE_EDGE_FALLING:
buf[0][idx] |= t->type_falling_val;
break;
case IRQ_TYPE_EDGE_RISING:
buf[0][idx] |= t->type_rising_val;
break;
case IRQ_TYPE_EDGE_BOTH:
buf[0][idx] |= (t->type_falling_val |
t->type_rising_val);
break;
case IRQ_TYPE_LEVEL_HIGH:
buf[0][idx] |= t->type_level_high_val;
break;
case IRQ_TYPE_LEVEL_LOW:
buf[0][idx] |= t->type_level_low_val;
break;
default:
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_GPL(regmap_irq_set_type_config_simple);
/**
* regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling
*
@ -634,7 +723,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
int ret = -ENOMEM;
int num_type_reg;
u32 reg;
u32 unmask_offset;
if (chip->num_regs <= 0)
return -EINVAL;
@ -651,11 +739,19 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
}
if (chip->not_fixed_stride) {
dev_warn(map->dev, "not_fixed_stride is deprecated; use ->get_irq_reg() instead");
for (i = 0; i < chip->num_regs; i++)
if (chip->sub_reg_offsets[i].num_regs != 1)
return -EINVAL;
}
if (chip->num_type_reg)
dev_warn(map->dev, "type registers are deprecated; use config registers instead");
if (chip->num_virt_regs || chip->virt_reg_base || chip->set_type_virt)
dev_warn(map->dev, "virtual registers are deprecated; use config registers instead");
if (irq_base) {
irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
if (irq_base < 0) {
@ -671,30 +767,30 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (chip->num_main_regs) {
d->main_status_buf = kcalloc(chip->num_main_regs,
sizeof(unsigned int),
sizeof(*d->main_status_buf),
GFP_KERNEL);
if (!d->main_status_buf)
goto err_alloc;
}
d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int),
d->status_buf = kcalloc(chip->num_regs, sizeof(*d->status_buf),
GFP_KERNEL);
if (!d->status_buf)
goto err_alloc;
d->mask_buf = kcalloc(chip->num_regs, sizeof(unsigned int),
d->mask_buf = kcalloc(chip->num_regs, sizeof(*d->mask_buf),
GFP_KERNEL);
if (!d->mask_buf)
goto err_alloc;
d->mask_buf_def = kcalloc(chip->num_regs, sizeof(unsigned int),
d->mask_buf_def = kcalloc(chip->num_regs, sizeof(*d->mask_buf_def),
GFP_KERNEL);
if (!d->mask_buf_def)
goto err_alloc;
if (chip->wake_base) {
d->wake_buf = kcalloc(chip->num_regs, sizeof(unsigned int),
d->wake_buf = kcalloc(chip->num_regs, sizeof(*d->wake_buf),
GFP_KERNEL);
if (!d->wake_buf)
goto err_alloc;
@ -703,11 +799,11 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
num_type_reg = chip->type_in_mask ? chip->num_regs : chip->num_type_reg;
if (num_type_reg) {
d->type_buf_def = kcalloc(num_type_reg,
sizeof(unsigned int), GFP_KERNEL);
sizeof(*d->type_buf_def), GFP_KERNEL);
if (!d->type_buf_def)
goto err_alloc;
d->type_buf = kcalloc(num_type_reg, sizeof(unsigned int),
d->type_buf = kcalloc(num_type_reg, sizeof(*d->type_buf),
GFP_KERNEL);
if (!d->type_buf)
goto err_alloc;
@ -724,13 +820,31 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
for (i = 0; i < chip->num_virt_regs; i++) {
d->virt_buf[i] = kcalloc(chip->num_regs,
sizeof(unsigned int),
sizeof(**d->virt_buf),
GFP_KERNEL);
if (!d->virt_buf[i])
goto err_alloc;
}
}
if (chip->num_config_bases && chip->num_config_regs) {
/*
* Create config_buf[num_config_bases][num_config_regs]
*/
d->config_buf = kcalloc(chip->num_config_bases,
sizeof(*d->config_buf), GFP_KERNEL);
if (!d->config_buf)
goto err_alloc;
for (i = 0; i < chip->num_config_regs; i++) {
d->config_buf[i] = kcalloc(chip->num_config_regs,
sizeof(**d->config_buf),
GFP_KERNEL);
if (!d->config_buf[i])
goto err_alloc;
}
}
d->irq_chip = regmap_irq_chip;
d->irq_chip.name = chip->name;
d->irq = irq;
@ -738,18 +852,53 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
d->chip = chip;
d->irq_base = irq_base;
if (chip->mask_base && chip->unmask_base &&
!chip->mask_unmask_non_inverted) {
/*
* Chips that specify both mask_base and unmask_base used to
* get inverted mask behavior by default, with no way to ask
* for the normal, non-inverted behavior. This "inverted by
* default" behavior is deprecated, but we have to support it
* until existing drivers have been fixed.
*
* Existing drivers should be updated by swapping mask_base
* and unmask_base and setting mask_unmask_non_inverted=true.
* New drivers should always set the flag.
*/
dev_warn(map->dev, "mask_base and unmask_base are inverted, please fix it");
/* Might as well warn about mask_invert while we're at it... */
if (chip->mask_invert)
dev_warn(map->dev, "mask_invert=true ignored");
d->mask_base = chip->unmask_base;
d->unmask_base = chip->mask_base;
} else if (chip->mask_invert) {
/*
* Swap the roles of mask_base and unmask_base if the bits are
* inverted. This is deprecated, drivers should use unmask_base
* directly.
*/
dev_warn(map->dev, "mask_invert=true is deprecated; please switch to unmask_base");
d->mask_base = chip->unmask_base;
d->unmask_base = chip->mask_base;
} else {
d->mask_base = chip->mask_base;
d->unmask_base = chip->unmask_base;
}
if (chip->irq_reg_stride)
d->irq_reg_stride = chip->irq_reg_stride;
else
d->irq_reg_stride = 1;
if (chip->type_reg_stride)
d->type_reg_stride = chip->type_reg_stride;
if (chip->get_irq_reg)
d->get_irq_reg = chip->get_irq_reg;
else
d->type_reg_stride = 1;
d->get_irq_reg = regmap_irq_get_irq_reg_linear;
if (!map->use_single_read && map->reg_stride == 1 &&
d->irq_reg_stride == 1) {
if (regmap_irq_can_bulk_read_status(d)) {
d->status_reg_buf = kmalloc_array(chip->num_regs,
map->format.val_bytes,
GFP_KERNEL);
@ -766,35 +915,34 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
/* Mask all the interrupts by default */
for (i = 0; i < chip->num_regs; i++) {
d->mask_buf[i] = d->mask_buf_def[i];
if (!chip->mask_base)
continue;
reg = sub_irq_reg(d, d->chip->mask_base, i);
if (chip->mask_invert)
ret = regmap_irq_update_bits(d, reg,
d->mask_buf[i], ~d->mask_buf[i]);
else if (d->chip->unmask_base) {
unmask_offset = d->chip->unmask_base -
d->chip->mask_base;
ret = regmap_irq_update_bits(d,
reg + unmask_offset,
d->mask_buf[i],
d->mask_buf[i]);
} else
ret = regmap_irq_update_bits(d, reg,
d->mask_buf[i], d->mask_buf[i]);
if (ret != 0) {
if (d->mask_base) {
reg = d->get_irq_reg(d, d->mask_base, i);
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], d->mask_buf[i]);
if (ret) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
reg, ret);
goto err_alloc;
}
}
if (d->unmask_base) {
reg = d->get_irq_reg(d, d->unmask_base, i);
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
reg, ret);
goto err_alloc;
}
}
if (!chip->init_ack_masked)
continue;
/* Ack masked but set interrupts */
reg = sub_irq_reg(d, d->chip->status_base, i);
reg = d->get_irq_reg(d, d->chip->status_base, i);
ret = regmap_read(map, reg, &d->status_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n",
@ -806,7 +954,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
d->status_buf[i] = ~d->status_buf[i];
if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) {
reg = sub_irq_reg(d, d->chip->ack_base, i);
reg = d->get_irq_reg(d, d->chip->ack_base, i);
if (chip->ack_invert)
ret = regmap_write(map, reg,
~(d->status_buf[i] & d->mask_buf[i]));
@ -831,14 +979,14 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (d->wake_buf) {
for (i = 0; i < chip->num_regs; i++) {
d->wake_buf[i] = d->mask_buf_def[i];
reg = sub_irq_reg(d, d->chip->wake_base, i);
reg = d->get_irq_reg(d, d->chip->wake_base, i);
if (chip->wake_invert)
ret = regmap_irq_update_bits(d, reg,
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i],
0);
else
ret = regmap_irq_update_bits(d, reg,
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i],
d->wake_buf[i]);
if (ret != 0) {
@ -851,7 +999,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (chip->num_type_reg && !chip->type_in_mask) {
for (i = 0; i < chip->num_type_reg; ++i) {
reg = sub_irq_reg(d, d->chip->type_base, i);
reg = d->get_irq_reg(d, d->chip->type_base, i);
ret = regmap_read(map, reg, &d->type_buf_def[i]);
@ -907,6 +1055,11 @@ err_alloc:
kfree(d->virt_buf[i]);
kfree(d->virt_buf);
}
if (d->config_buf) {
for (i = 0; i < chip->num_config_bases; i++)
kfree(d->config_buf[i]);
kfree(d->config_buf);
}
kfree(d);
return ret;
}
@ -947,7 +1100,7 @@ EXPORT_SYMBOL_GPL(regmap_add_irq_chip);
void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
{
unsigned int virq;
int hwirq;
int i, hwirq;
if (!d)
return;
@ -977,6 +1130,11 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
kfree(d->mask_buf);
kfree(d->status_reg_buf);
kfree(d->status_buf);
if (d->config_buf) {
for (i = 0; i < d->chip->num_config_bases; i++)
kfree(d->config_buf[i]);
kfree(d->config_buf);
}
kfree(d);
}
EXPORT_SYMBOL_GPL(regmap_del_irq_chip);

View File

@ -1440,6 +1440,8 @@ struct regmap_irq_sub_irq_map {
unsigned int *offset;
};
struct regmap_irq_chip_data;
/**
* struct regmap_irq_chip - Description of a generic regmap irq_chip.
*
@ -1467,32 +1469,50 @@ struct regmap_irq_sub_irq_map {
* main_status set.
*
* @status_base: Base status register address.
* @mask_base: Base mask register address.
* @mask_writeonly: Base mask register is write only.
* @unmask_base: Base unmask register address. for chips who have
* separate mask and unmask registers
* @mask_base: Base mask register address. Mask bits are set to 1 when an
* interrupt is masked, 0 when unmasked.
* @unmask_base: Base unmask register address. Unmask bits are set to 1 when
* an interrupt is unmasked and 0 when masked.
* @ack_base: Base ack address. If zero then the chip is clear on read.
* Using zero value is possible with @use_ack bit.
* @wake_base: Base address for wake enables. If zero unsupported.
* @type_base: Base address for irq type. If zero unsupported.
* @virt_reg_base: Base addresses for extra config regs.
* @type_base: Base address for irq type. If zero unsupported. Deprecated,
* use @config_base instead.
* @virt_reg_base: Base addresses for extra config regs. Deprecated, use
* @config_base instead.
* @config_base: Base address for IRQ type config regs. If null unsupported.
* @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.
* Deprecated; prefer describing an inverted mask register as
* an unmask register.
* @mask_unmask_non_inverted: Controls mask bit inversion for chips that set
* both @mask_base and @unmask_base. If false, mask and unmask bits are
* inverted (which is deprecated behavior); if true, bits will not be
* inverted and the registers keep their normal behavior. Note that if
* you use only one of @mask_base or @unmask_base, this flag has no
* effect and is unnecessary. Any new drivers that set both @mask_base
* and @unmask_base should set this to true to avoid relying on the
* deprecated behavior.
* @use_ack: Use @ack register even if it is zero.
* @ack_invert: Inverted ack register: cleared bits for ack.
* @clear_ack: Use this to set 1 and 0 or vice-versa to clear interrupts.
* @wake_invert: Inverted wake register: cleared bits are wake enabled.
* @type_invert: Invert the type flags.
* @type_in_mask: Use the mask registers for controlling irq type. For
* interrupts defining type_rising/falling_mask use mask_base
* for edge configuration and never update bits in type_base.
* @type_invert: Invert the type flags. Deprecated, use config registers
* instead.
* @type_in_mask: Use the mask registers for controlling irq type. Use this if
* the hardware provides separate bits for rising/falling edge
* or low/high level interrupts and they should be combined into
* a single logical interrupt. Use &struct regmap_irq_type data
* to define the mask bit for each irq type.
* @clear_on_unmask: For chips with interrupts cleared on read: read the status
* registers before unmasking interrupts to clear any bits
* set when they were masked.
* @not_fixed_stride: Used when chip peripherals are not laid out with fixed
* stride. Must be used with sub_reg_offsets containing the
* offsets to each peripheral.
* offsets to each peripheral. Deprecated; the same thing
* can be accomplished with a @get_irq_reg callback, without
* the need for a @sub_reg_offsets table.
* @status_invert: Inverted status register: cleared bits are active interrupts.
* @runtime_pm: Hold a runtime PM lock on the device when accessing it.
*
@ -1500,17 +1520,28 @@ struct regmap_irq_sub_irq_map {
* @irqs: Descriptors for individual IRQs. Interrupt numbers are
* assigned based on the index in the array of the interrupt.
* @num_irqs: Number of descriptors.
* @num_type_reg: Number of type registers.
* @num_type_reg: Number of type registers. Deprecated, use config registers
* instead.
* @num_virt_regs: Number of non-standard irq configuration registers.
* If zero unsupported.
* @type_reg_stride: Stride to use for chips where type registers are not
* contiguous.
* If zero unsupported. Deprecated, use config registers
* instead.
* @num_config_bases: Number of config base registers.
* @num_config_regs: Number of config registers for each config base register.
* @handle_pre_irq: Driver specific callback to handle interrupt from device
* before regmap_irq_handler process the interrupts.
* @handle_post_irq: Driver specific callback to handle interrupt from device
* after handling the interrupts in regmap_irq_handler().
* @set_type_virt: Driver specific callback to extend regmap_irq_set_type()
* and configure virt regs.
* and configure virt regs. Deprecated, use @set_type_config
* callback and config registers instead.
* @set_type_config: Callback used for configuring irq types.
* @get_irq_reg: Callback for mapping (base register, index) pairs to register
* addresses. The base register will be one of @status_base,
* @mask_base, etc., @main_status, or any of @config_base.
* The index will be in the range [0, num_main_regs[ for the
* main status base, [0, num_type_settings[ for any config
* register base, and [0, num_regs[ for any other base.
* If unspecified then regmap_irq_get_irq_reg_linear() is used.
* @irq_drv_data: Driver specific IRQ data which is passed as parameter when
* driver specific pre/post interrupt handler is called.
*
@ -1533,20 +1564,21 @@ struct regmap_irq_chip {
unsigned int wake_base;
unsigned int type_base;
unsigned int *virt_reg_base;
const unsigned int *config_base;
unsigned int irq_reg_stride;
bool mask_writeonly:1;
bool init_ack_masked:1;
bool mask_invert:1;
bool use_ack:1;
bool ack_invert:1;
bool clear_ack:1;
bool wake_invert:1;
bool runtime_pm:1;
bool type_invert:1;
bool type_in_mask:1;
bool clear_on_unmask:1;
bool not_fixed_stride:1;
bool status_invert:1;
unsigned int init_ack_masked:1;
unsigned int mask_invert:1;
unsigned int mask_unmask_non_inverted:1;
unsigned int use_ack:1;
unsigned int ack_invert:1;
unsigned int clear_ack:1;
unsigned int wake_invert:1;
unsigned int runtime_pm:1;
unsigned int type_invert:1;
unsigned int type_in_mask:1;
unsigned int clear_on_unmask:1;
unsigned int not_fixed_stride:1;
unsigned int status_invert:1;
int num_regs;
@ -1555,16 +1587,24 @@ struct regmap_irq_chip {
int num_type_reg;
int num_virt_regs;
unsigned int type_reg_stride;
int num_config_bases;
int num_config_regs;
int (*handle_pre_irq)(void *irq_drv_data);
int (*handle_post_irq)(void *irq_drv_data);
int (*set_type_virt)(unsigned int **buf, unsigned int type,
unsigned long hwirq, int reg);
int (*set_type_config)(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx);
unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data,
unsigned int base, int index);
void *irq_drv_data;
};
struct regmap_irq_chip_data;
unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data,
unsigned int base, int index);
int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx);
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
int irq_base, const struct regmap_irq_chip *chip,